Axum 设计与实现

从源码视角剖析 Rust Web 框架 Axum。

打开任何一个 Rust 后端项目的 src/main.rs,你几乎都会看到这样的代码:

let app = Router::new()
    .route("/", get(handler))
    .with_state(state);

let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
serve(listener, app).await.unwrap();

三行代码,一个 HTTP 服务就跑起来了。路由、提取、响应、中间件——框架帮你把这些全部安排妥当。但当你想给路由加个前缀、想让某个提取器返回自定义错误、想在 serve 之外接管连接生命周期、想搞清楚 Handler<T, S> 那个幽灵参数 T 到底是什么……你就得往下挖了。

这个专栏的目的,就是把这三行代码底下的每一层都打开给你看。

你会看到 Router<S> 如何用类型状态模式在编译期强制你提供 State;你会看到 matchit 如何在纳秒级完成路径匹配;你会看到 Handler<T, S> 如何用一个宏把最多 16 个参数的 async fn 展开成 tower::Service;你会看到 FromRequestFromRequestParts 的分裂为什么不是设计冗余而是 Body 所有权约束的必然结果;你会看到 from_fn 中间件的 Next 如何在类型系统层面保证调用链不中断;你会看到 Infallible 错误模型如何让错误永远不会逃逸到 hyper;你会看到 Serve 如何用 tokio::select! 实现优雅关闭。

这是《Rust 源码之道》本系列的第六卷,也是"Rust 后端三部曲"的最后一环:

flowchart TB
  REQ[HTTP 请求] --> RT
  subgraph RT [Router&lt;S&gt; · 类型状态 · Arc 包裹]
    direction LR
    PR[PathRouter<br/>matchit 基数树] --> MR[MethodRouter<br/>按方法分发]
  end
  RT --> MW[中间件栈<br/>tower::Layer / from_fn]
  MW --> EXT
  subgraph EXT [提取器 · 一条物理约束]
    direction LR
    FP["FromRequestParts<br/>借 &amp;mut Parts · 可多个"] --> FR[FromRequest<br/>消费 Request · 只能一个]
  end
  EXT --> H["Handler&lt;T, S&gt;<br/>all_the_tuples! 展开"]
  H --> IR[IntoResponse]
  IR --> RES[HTTP 响应]
  ST[AppState<br/>FromRef 子状态抽取] -.注入.-> EXT
  ERR[HandleError<br/>为什么坚持 Infallible] -.兜底.-> MW

这张图里最值钱的一条是提取器那一格。 body 在协议层是一次性的——这条物理约束直接决定了 axum 为什么要分 FromRequestPartsFromRequest 两个 trait、为什么消费 body 的提取器只能放在参数列表最后一个。理解了这一条,那些"编译器报了一大段看不懂的错"的时刻就消失了。

读完你能做到什么

  1. 看懂 Router 的类型状态。 Router<S> 为什么要带状态泛型、为什么用 Arc 包裹、PathRouter 的双层索引(matchit 基数树 + 双向 HashMap)、一次路径匹配的完整流程(第2章 Router第3章 MethodRouter第4章 嵌套与合并)。
  2. 解释"为什么我的函数不是 handler"。 Handler<T, S> 的定义、all_the_tuples! 怎么把多参数展开、从 Handler 到 Service 的适配器(第5章 Handler)。
  3. 写自己的提取器。 两个 trait 的分工与那条 body 物理约束、Rejection 与 IntoResponse 的硬契约、Result<T, E> 用 Infallible 把拒绝变成值、Option<T> 的语义、元组提取器(第6章 FromRequest第7章 内置提取器第8章 高级提取器)。
  4. 控制响应。 IntoResponse 的实现面、响应类型与响应各部分的组装(第9章 IntoResponse第10章 响应类型第11章 响应部件)。
  5. 搞清 axum 的错误哲学。 为什么坚持 Infallible、哪些 Service 真的会失败、HandleError 的源码、BoxError 与 downcast 的陷阱,以及该监控哪些指标(第12章 错误处理)。
  6. 写中间件并知道什么时候不该用 from_fn。 Next 这个抽象、签名要求、from_fn_with_state,以及它与 tower::Layer 的分界(第13章 函数式中间件第14章 map 系列)。
  7. 组织好 state 与 body。 FromRef 的子状态抽取、多字段 state 与 #[derive(FromRef)]、嵌套 FromRef,以及 Body 的类型擦除与 try_downcast 优化、流式构造(第18章 State第17章 Body 与流式)。
  8. 上生产。 serve 与监听器/执行器、宏、axum-extra、测试与生产实践(第15章 serve第16章 监听器第19章 宏第20章 extra第21章 测试第22章 生产)。

适合谁读

前置知识:本专栏假设读者熟悉 Rust 的 trait、泛型、生命周期、PinFutureasync/await。建议先阅读卷五——本专栏不会重复解释 Service / Layer / Body 这些概念,但会在每一处与 Hyper+Tower 心智模型相关的地方给出章节索引。

目录

开篇

第一部分:路由系统

第二部分:Handler 与提取器

第三部分:响应与错误

第四部分:中间件

第五部分:运行与状态

第六部分:元编程与扩展

第七部分:工程实践

源码版本

本专栏所有源码引用均基于以下版本(2026 年 4 月 23 日锁定):

Crate 版本 Git Commit
axum 0.8.9 de9f13d
axum-core 0.5.6 de9f13d
axum-macros 0.5.1 de9f13d
axum-extra 0.12.6 de9f13d

读者可通过以下命令获取与本专栏完全一致的源码:

git clone https://github.com/tokio-rs/axum.git
cd axum && git checkout de9f13d

正文每一段源码引用都会标注文件路径和行号,读者可在对应版本的代码中逐行验证。

与其他专栏的关联

版权声明

本专栏内容为 杨艺韬 版权所有,保留一切权利。未经书面许可,不得全文或大段转载、改编、翻译,或用于任何商业用途(含以本专栏内容训练模型、生成衍生课程或商品)。

欢迎分享本专栏的链接。引用少量内容用于评论、教学或研究时,请署名 杨艺韬 并附上原文链接。