HTTP/2
HTTP/2 的帧格式、HPACK、流状态机和流控记账都在独立的 h2 crate(本卷对照 h2 0.4.20)里,hyper 的 proto/h2 只有两千多行:把 h2 的流接到 hyper 的 Service 和 Body 上,决定哪些 future 交给执行器,再加一个 ping 模块。本部分只讲这一层,h2 的内部按版本号在正文里点到为止。
和 HTTP/1 连接 最大的区别是任务的划分。HTTP/1 一条连接一个 future,请求严格串行;HTTP/2 一条连接上有很多个流,hyper 给每个流单独一个任务,于是要 Executor,也换来了连接内的并发。
- HTTP/2 服务端:
Server先握手再循环poll_accept;每个新流在连接任务里调service.call,返回的 future 包成H2Streamspawn 出去;响应 body 由PipeToSendStream按流控容量写进 h2;服务出错只重置这一个流;graceful_shutdown发两次 GOAWAY,等在途的流结束。 - HTTP/2 客户端:
SendRequest可以Clone,背后是一个无界通道;h2 连接本身在握手时 spawn 的任务里跑,你 spawn 的Connection只负责把请求交给 h2;每个请求再 spawn 一个等响应的任务,body 没一次发完时还有一个发 body 的任务;drop 请求 future 发RST_STREAM(CANCEL),连接照常可用。 - ping:保活与 BDP:一个
Ponger兼管 keep-alive 和自适应窗口;keep-alive 在读不到帧时发 PING,超时就关连接(要Timer);BDP 拿 PING 的往返时间估算带宽时延积,把流控窗口翻倍调大。
flowchart TB io["IO:rt::Read + rt::Write"] --> h2conn["h2::Connection"] h2conn --> srv["服务端:Server"] h2conn --> cli["客户端:ConnTask"] srv --> stream["每个流一个 H2Stream 任务"] cli --> send["每个请求一个 Send 任务"] send --> pipe["body 未发完:Pipe 任务"] stream --> pipe2["PipeToSendStream 写响应 body"] h2conn --> ping["ping:keep-alive 与 BDP"]
读之前最好已经读过 Executor 与 Timer:哪些地方调用 execute、为什么执行器的约束写成 Http2ServerConnExec 这种 sealed trait、没有 timer 时 HTTP/2 keep-alive 为什么 panic,都在那一页。
💬 关联讨论
发起讨论加载中...