Table of contents
Open Table of contents
TL;DR
Rust 的异步网络栈是 mio → tokio → hyper → tower → axum 五层,每层职责单一、可独立替换。C++ Asio 把事件通知、调度、定时器、buffer、协程支持打包在一个库里。两种设计没有绝对优劣——Rust 的分层来自语言特性(ownership 使模块边界天然清晰),Asio 的一体化来自 C++ 生态的碎片化(没有统一运行时,不如自己全做)。
分层架构总览
┌──────────────────────────────────────────────────────────┐
│ 应用代码 │
├──────────────────────────────────────────────────────────┤
│ axum │ Web 框架:路由、提取器、响应 │
│ │ 零宏路由、编译期参数解析、IntoResponse │
├──────────────────────────────────────────────────────────┤
│ tower │ 中间件:Service trait + Layer 组合 │
│ │ 限流、重试、超时、背压——协议无关 │
├──────────────────────────────────────────────────────────┤
│ tower-http │ HTTP 专用中间件:Trace、Compression、CORS │
├──────────────────────────────────────────────────────────┤
│ hyper │ HTTP 协议:HTTP/1.1 + HTTP/2 编解码 │
│ │ Body trait、连接管理 │
├──────────────────────────────────────────────────────────┤
│ tokio │ 异步运行时:work-stealing 调度器 │
│ │ 任务系统、定时器(时间轮)、同步原语 │
├──────────────────────────────────────────────────────────┤
│ mio │ I/O 事件通知:epoll / kqueue / IOCP 薄封装 │
│ │ Poll + Token + Events,极简 Reactor │
├──────────────────────────────────────────────────────────┤
│ 操作系统 │ epoll / kqueue / IOCP / io_uring │
└──────────────────────────────────────────────────────────┘
每层职责与边界
| 层 | crate | 职责 | 不做什么 |
|---|
| I/O 事件通知 | mio | 跨平台 fd 就绪通知 | 不做 buffer、timer、async/await、调度 |
| 异步运行时 | tokio | 任务调度 + I/O 驱动 + timer + 同步原语 | 不做 HTTP 协议、路由、中间件 |
| HTTP 协议 | hyper | HTTP/1.1 + HTTP/2 编解码 | 不做路由、参数提取、中间件组合 |
| 中间件抽象 | tower | Service trait + Layer 组合 | 不做 HTTP 特定逻辑 |
| HTTP 中间件 | tower-http | Compression / CORS / Trace 等 | 不做路由 |
| Web 框架 | axum | 路由 + 提取器 + 响应 | 不做 HTTP 协议解析、不发明中间件系统 |
与 C++ Asio 的架构对比
C++ Asio(一个库包含一切) Rust(分层栈)
┌─────────────────────────┐ ┌──────────────┐
│ io_context │ │ axum │ ← Web 框架
│ ├── epoll_reactor │ ├──────────────┤
│ ├── strand │ │ tower │ ← 中间件
│ ├── timer_queue │ ├──────────────┤
│ ├── tcp::socket │ │ hyper │ ← HTTP
│ ├── buffer │ ├──────────────┤
│ ├── ssl::stream │ │ tokio │ ← 运行时
│ ├── resolver │ ├──────────────┤
│ └── coroutine support │ │ mio │ ← I/O 事件
└─────────────────────────┘ └──────────────┘
| 维度 | C++ Asio | Rust 栈 |
|---|
| 异步模式 | Proactor(向 IOCP 对齐) | Reactor(mio)+ Future/async(tokio) |
| 线程模型 | 用户手动管理(多线程 run()) | work-stealing 调度器(tokio) |
| 中间件 | 无统一抽象 | tower Service trait |
| 定时器 | 最小堆 | 分层时间轮(tokio) |
| 内存安全 | 用户负责(shared_from_this 模式) | 所有权系统 + 借用检查器 |
| 并发保护 | strand | Send + Sync trait 静态检查 |
| Buffer | 非拥有式(指针+长度) | 所有权明确(Bytes, Vec) |
| 协程 | 完成令牌机制统一 5 种风格 | async/await 是语言内置 |
| 标准化 | Networking TS 未进标准 | 无标准运行时(tokio 是事实标准) |
核心设计分歧:为什么 Rust 不需要 Proactor
Asio 选 Proactor 是为了统一 IOCP 和 epoll 的 API。Rust 的做法不同:
- mio 选 Reactor:更自然地匹配 epoll/kqueue;IOCP 的 Proactor 语义在 mio 内部适配(通过 AFD 轮询模拟 readiness)
- async/await 让 Reactor 写起来像 Proactor:用户代码写
let n = socket.read(&mut buf).await,看起来像 Proactor(「帮我读,读完告诉我」),但底层是 Reactor(mio 通知就绪 → tokio 驱动执行 read → 结果返回给 Future)
- 所有权系统消除了 Proactor 的缓冲区生命周期问题:Asio 的
async_read 需要用户保证 buffer 在操作完成前有效(共享指针模式),Rust 的 read(&mut buf).await 通过借用检查器在编译期保证 buffer 活得够久
文档索引
mio(I/O 事件通知)
| 文档 | 核心内容 |
|---|
| mio 是什么 | 设计哲学、为什么选 Reactor |
| 核心架构 | Poll / Token / Interest / Events / Source trait |
| 平台后端 | epoll / kqueue / IOCP 适配细节 |
| 为什么不直接用 mio | mio 的局限与 tokio 的补充 |
| 关键设计决策 | Token vs callback、强制非阻塞、无全局状态 |
| 代码示例 | mio / tokio / Asio 三方对比 |
tokio(异步运行时)
| 文档 | 核心内容 |
|---|
| 运行时设计 | work-stealing 调度器、与 Asio/Go 对比 |
| 任务系统 | spawn / JoinHandle / 协作式调度 / JoinSet |
| I/O 与同步原语 | I/O 驱动、时间轮、Mutex/channel、select! |
| 陷阱与实践 | 10 大陷阱、优雅关闭、tracing |
tower(中间件抽象)
hyper(HTTP 协议)
axum(Web 框架)
C++ Asio(对照组)
推荐阅读顺序
如果你来自 C++ Asio 背景:
- Asio Proactor 模式 → mio 为什么选 Reactor(理解设计分歧)
- Asio io_context → tokio 运行时(理解调度模型差异)
- Asio 组件 → tokio I/O + 同步原语(理解组件对应关系)
- tower Service trait(Asio 没有的层——中间件抽象)
- hyper → axum(Asio 没有的层——HTTP 框架)
如果你从零开始学 Rust 异步:
- mio 是什么 → 为什么不直接用 mio(建立底层认知)
- tokio 运行时 → 任务系统 → I/O 与同步原语(理解运行时全貌)
- tower Service trait → Layer → 内置中间件(理解中间件范式)
- hyper Body trait → axum 设计哲学 → Handler 与 Extractor(理解 Web 层)
- tokio 陷阱 → axum 中间件与实践(实战储备)