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 后端三部曲"的中间一环:

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_readycall 的两步协议,axum 的中间件、tonic 的拦截器、hyper 的连接处理就都是同一件事的不同外衣。

读完你能做到什么

  1. 手写一个 Service 与一个中间件。 三个关联类型为什么是这三个、poll_ready 的灵魂、call 的边界条件,以及 Timeout 这个最小中间件的完整实现(第2章 Service trait第3章 Layer 与 Builder)。
  2. 真正理解背压。 从一次真实事故讲起:ConcurrencyLimit 的两步协议、poll_ready 在 async 世界的惯用法、backpressure 怎么穿透三个层次,以及三种反模式(第4章 poll_ready 与背压)。
  3. 用对那一排中间件。 超时/重试/限流的组合陷阱、Buffer 与 LoadShed 的取舍、负载均衡与服务发现、Filter 与 Steer(第5章 Timeout/Retry/RateLimit第6章 Buffer/LoadShed第7章 Balance第8章 Filter/Steer)。
  4. 读懂 HTTP/1 的连线实现。 Conn 承载的全部状态、Reading × Writing × KA 的合法转移、Dispatcher 的 poll_loop 一次做多少事、keep-alive 与各种超时(第11章 HTTP/1 wire第12章 Dispatcher第14章 keep-alive)。
  5. 算清 HTTP/2 的内存账。 多路复用、Connection 级与 Stream 级两级流控、max_frame_sizemax_send_buf_size 的差别、max_concurrent_streams × stream_window 的内存数学,以及 CVE-2023-44487 的防御参数(第15章 HPACK第16章 流控第17章 PING/GOAWAY)。
  6. 调好客户端连接池。 Pool 的数据结构、Checkout 流程、归还与 idle 管理、Connector 的 DNS + TCP + TLS 三层分离(第20章 连接池第21章 客户端分发)。
  7. 把它接到上层框架并调优。 axum 与 tonic 是怎么建在这两个库上的、生产参数怎么定(第22章 axum 与 tonic第23章 生产调优第24章 设计哲学)。

适合谁读

前置知识:本专栏假设读者熟悉 Rust 的 trait、泛型、生命周期、PinFutureasync/await。如果对 Tokio 的 runtime、I/O driver 或 Waker 机制还有疑问,建议先阅读卷四——本专栏不会重复解释这些概念,但会在每一处与 Tokio 心智模型相关的地方给出章节索引。

目录

开篇

第一部分:Tower 的核心抽象

第二部分:Tower 中间件源码实录

第三部分:HTTP 数据模型

第四部分:Hyper HTTP/1 实现

第五部分:Hyper HTTP/2 实现

第六部分:Upgrade 与桥接

第七部分:客户端

第八部分:工程实践与设计哲学

源码版本

本专栏所有源码引用均基于以下版本(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.rssrc/hpack/huffman/mod.rssrc/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

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

与其他专栏的关联

版权声明

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

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