Hyper 与 Tower:工业级 HTTP 栈
从源码视角剖析 Rust 的 HTTP 基础设施。
打开任何一个 Rust 后端服务的 Cargo.toml,几乎都会看到这两行:
hyper = "1"
tower = "0.5"
你可能只是在 Axum 里写了一个 async fn handler(Request) -> Response,可能只是在 reqwest 里敲了一句 client.get(url).send().await,但真正把你的 async fn 搬到网络上、让它接住成千上万并发连接的,是这两个库。它们是 Rust 后端生态的水和电——无处不在,却少有人真正看清它们的内部结构。
这个专栏的目的,就是把"水和电"打开给你看。
你会看到 Tower 如何用一个只有两个方法的 Service trait,为整个 Rust 生态统一了"异步函数"的抽象;你会看到 Layer 如何在类型系统层面把中间件堆成洋葱;你会看到 Hyper 如何把 HTTP/1 和 HTTP/2 这两套截然不同的协议,用同一套 Connection<T, S> 状态机驱动;你会看到 poll_ready 这个看似多余的 API,为什么是整个背压体系的基石;你会看到 hyper::body::Incoming 如何把字节流包装成可以被你的业务代码消费的 Body;你会看到 hyper-util 为什么必须存在——它桥接了 hyper 1.0 自己的 Service trait 与 tower 的 Service trait 之间那道看似细微却后果深远的裂缝。
这是《Rust 源码之道》本系列的第五卷,也是"Rust 后端三部曲"的中间一环:
- 卷三 · Rust 编译器与运行时揭秘:讲编译器如何把你写的
async fn展开成状态机。 - 卷四 · Tokio 源码深度解析:讲运行时如何把状态机调度到线程与 I/O 之上。
- 卷五 · Hyper 与 Tower(本专栏):讲 HTTP 协议栈如何把字节流翻译成可以被业务代码消费的请求与响应,讲 Service / Layer 抽象如何把"中间件"这件事做到真正的协议无关。
- 后续:Axum、Tonic、reqwest 的源码专栏将直接建立在这三卷的心智模型之上。
flowchart TB
subgraph TW [tower · 中间件的地基]
direction LR
SV["Service trait<br/>三个关联类型"] --> PR["poll_ready<br/>两步协议 · 背压的灵魂"]
PR --> LY[Layer / ServiceBuilder]
LY --> MW[Timeout · Retry · RateLimit<br/>Buffer · LoadShed · Balance]
end
TW --> HY
subgraph HY [hyper · 字节与协议]
direction TB
H1["HTTP/1 wire<br/>Conn 状态机<br/>Reading × Writing × KA"] --- H2[HTTP/2<br/>多路复用 · 两级流控<br/>HPACK · GOAWAY]
H1 --> DP[Dispatcher<br/>poll_loop]
H2 --> DP
end
HY --> POOL[连接池<br/>Checkout · idle 管理<br/>Connector: DNS + TCP + TLS]
TW -.被谁用.-> UP[axum · tonic]
这两个库分工得非常干净:tower 管"怎么组合",hyper 管"怎么落到字节"。 而把它们缝在一起的是 Service 这一个 trait —— 理解了 poll_ready 与 call 的两步协议,axum 的中间件、tonic 的拦截器、hyper 的连接处理就都是同一件事的不同外衣。
读完你能做到什么
- 手写一个 Service 与一个中间件。 三个关联类型为什么是这三个、
poll_ready的灵魂、call的边界条件,以及Timeout这个最小中间件的完整实现(第2章 Service trait、第3章 Layer 与 Builder)。 - 真正理解背压。 从一次真实事故讲起:
ConcurrencyLimit的两步协议、poll_ready在 async 世界的惯用法、backpressure 怎么穿透三个层次,以及三种反模式(第4章 poll_ready 与背压)。 - 用对那一排中间件。 超时/重试/限流的组合陷阱、Buffer 与 LoadShed 的取舍、负载均衡与服务发现、Filter 与 Steer(第5章 Timeout/Retry/RateLimit、第6章 Buffer/LoadShed、第7章 Balance、第8章 Filter/Steer)。
- 读懂 HTTP/1 的连线实现。
Conn承载的全部状态、Reading × Writing × KA的合法转移、Dispatcher 的poll_loop一次做多少事、keep-alive 与各种超时(第11章 HTTP/1 wire、第12章 Dispatcher、第14章 keep-alive)。 - 算清 HTTP/2 的内存账。 多路复用、Connection 级与 Stream 级两级流控、
max_frame_size与max_send_buf_size的差别、max_concurrent_streams × stream_window的内存数学,以及 CVE-2023-44487 的防御参数(第15章 HPACK、第16章 流控、第17章 PING/GOAWAY)。 - 调好客户端连接池。 Pool 的数据结构、Checkout 流程、归还与 idle 管理、Connector 的 DNS + TCP + TLS 三层分离(第20章 连接池、第21章 客户端分发)。
- 把它接到上层框架并调优。 axum 与 tonic 是怎么建在这两个库上的、生产参数怎么定(第22章 axum 与 tonic、第23章 生产调优、第24章 设计哲学)。
适合谁读
- Rust Web 开发者:每天写 Axum、actix-web 或 Tonic,想理解这些框架"底下的底下"到底长什么样。
- 中间件作者:准备给自己的服务写一个 retry、rate limiting、tracing 中间件,想从 Tower 生态里偷师一套经过工业打磨的模式。
- 性能工程师:在生产环境被 HTTP/2 的 flow control、连接池耗尽、优雅关闭、尾延迟这些问题折磨过,需要从协议栈源码层面找根因。
- 开源贡献者:准备给 Hyper、Tower、Axum 提 PR,或者在自己的项目里封装一个 HTTP 客户端/服务端库。
- 读过《Tokio 源码深度解析》的读者:希望把 Future/Waker/Runtime 的心智模型,延伸到真实的网络协议与中间件编排之上。
前置知识:本专栏假设读者熟悉 Rust 的 trait、泛型、生命周期、Pin、Future 与 async/await。如果对 Tokio 的 runtime、I/O driver 或 Waker 机制还有疑问,建议先阅读卷四——本专栏不会重复解释这些概念,但会在每一处与 Tokio 心智模型相关的地方给出章节索引。
目录
开篇
第一部分:Tower 的核心抽象
- 第2章 Service trait:
async fn(Req) -> Res的协议无关抽象 - 第3章 Layer 与 ServiceBuilder:类型级中间件组合
- 第4章 poll_ready 与 backpressure:显式容量信号的工程意义
第二部分:Tower 中间件源码实录
- 第5章 Timeout / Retry / RateLimit:基础中间件的源码剖析
- 第6章 Buffer / LoadShed / ConcurrencyLimit:容量工程
- 第7章 Balance / Discover / ready_cache:负载均衡抽象
- 第8章 Filter / MapRequest / Steer:请求路由与变换
第三部分:HTTP 数据模型
- 第9章 http crate:Request / Response / HeaderMap 的零分配设计
- 第10章 http-body 与 Body trait:frame、trailers、size_hint
第四部分:Hyper HTTP/1 实现
- 第11章 HTTP/1 wire:parser、encoder、chunked 编码
- 第12章 Connection Dispatcher 与 Role:请求分发状态机
- 第13章 hyper 的 Service trait:为什么 1.0 不复用 tower::Service
- 第14章 keep-alive、半关闭与超时矩阵
第五部分:Hyper HTTP/2 实现
- 第15章 h2 crate 与 HPACK:HTTP/2 的线路层
- 第16章 多流调度与流控:backpressure 在 HTTP/2 的落地
- 第17章 PING / GOAWAY / 超时:HTTP/2 的生存线
第六部分:Upgrade 与桥接
第七部分:客户端
第八部分:工程实践与设计哲学
- 第22章 Axum / Tonic 如何构建在 Hyper + Tower 之上
- 第23章 生产环境调优:超时矩阵、背压、优雅关闭
- 第24章 设计哲学:从 Tower 到 async trait 的演进
源码版本
本专栏所有源码引用均基于以下版本(2026 年 4 月 20 日锁定):
| Crate | 版本 | Git Commit |
|---|---|---|
| hyper | 1.9.0 | 0d6c7d5 |
| tower | 0.5.3 | 251296d |
| tower-service | 0.3.x | 随 tower |
| tower-layer | 0.3.x | 随 tower |
| http | 1.4.0 | 29dd307 |
| http-body | 1.0.1 | c8cb37f |
| http-body-util | 0.1.3 | c8cb37f |
| hyper-util | 0.1.20 | 8ae9e8b |
| tower-http | 0.6.8 | 33166c8 |
| h2 | 0.3.27 | v0.3.27 |
关于 h2 的版本要单独说一句:hyper 1.9.0 的 Cargo.toml 里依赖的是
h2 = { version = "0.4.6", optional = true },而本专栏第 15–17 章的 HPACK
与流控分析写的是 h2 0.3.27。
两者并存不是笔误——被引用的四个文件里,src/hpack/decoder.rs、
src/hpack/huffman/mod.rs、src/proto/streams/flow_control.rs 在 0.3.27 与
0.4.7 之间逐字节相同,行号照样对得上;只有 src/hpack/encoder.rs 有
内容差异(两版都是 720 行,本专栏对它的引用不带行号)。想完全对齐 hyper
1.9.0 的依赖树时,checkout v0.4.7 读这几处也成立。
读者可通过以下命令获取与本专栏完全一致的源码:
git clone https://github.com/hyperium/hyper.git
cd hyper && git checkout 0d6c7d5
git clone https://github.com/tower-rs/tower.git
cd tower && git checkout 251296d
git clone https://github.com/tower-rs/tower-http.git
cd tower-http && git checkout 33166c8
git clone https://github.com/hyperium/http.git
cd http && git checkout 29dd307
git clone https://github.com/hyperium/http-body.git
cd http-body && git checkout c8cb37f
git clone https://github.com/hyperium/h2.git
cd h2 && git checkout v0.3.27
正文每一段源码引用都会标注文件路径和行号,读者可在对应版本的代码中逐行验证。
与其他专栏的关联
- 前置:《Tokio 源码深度解析》第 2-3 章(Future / Waker)、第 4-7 章(Runtime / Task)、第 8-10 章(I/O Driver)——本专栏所有
.await都跑在 Tokio 之上,不会重复解释这些机制。 - 前置:《Rust 编译器与运行时揭秘》第 9 章(async 状态机)、第 10 章(Pin / Waker / Future)——在本专栏第 13 章讨论
hyper::Service为什么用&self时,我们会直接引用 async trait 的单态化结论。 - 平行:《Serde 元编程》——Tower 的
Layertrait 和 Serde 的Serializertrait 共享同一个设计哲学:用 trait 把 M×N 矩阵拆成 M+N。第 3 章会专门对照两者的设计。 - 后续:Axum、Tonic、reqwest 的源码专栏会直接建立在本专栏已经讲清楚的
Service/Layer/Body心智模型之上。
版权声明
本专栏内容为 杨艺韬 版权所有,保留一切权利。未经书面许可,不得全文或大段转载、改编、翻译,或用于任何商业用途(含以本专栏内容训练模型、生成衍生课程或商品)。
欢迎分享本专栏的链接。引用少量内容用于评论、教学或研究时,请署名 杨艺韬 并附上原文链接。