客户端
客户端主线是「请求 → 字节 → 响应」。hyper 1.x 只保留了最底下的一段:给它一条连接,它负责在上面发请求、收响应。连接从哪里来、用完放到哪里、失败了换哪一条,都在 hyper-util 的 client::legacy 里,reqwest 用的就是它。前四章从下往上,后两章分别是可以插进 legacy Client 的代理连接器,和不依赖它的连接池积木:
- conn::http1 客户端:
handshake返回SendRequest和要自己 spawn 的Connection;请求通道是无界mpsc加want标志,连接空闲时才就绪,每次只放进一个请求;请求在各个环节失败时报什么,dispatch task is gone什么时候出现。 - legacy Client:
request算出池的键、拿连接、补Host、改 URI 形式再发;retry_canceled_requests只重发还没写出、且连接是复用来的请求,实测这个窗口很窄。 - 连接池:按
(scheme, authority)分组的空闲连接,取连接与新建连接赛跑,空闲超时和清理任务,HTTP/2 连接的共享。 - HttpConnector:连接器就是
tower::Service<Uri>,DNS 解析、Happy Eyeballs、TCP 选项;TLS 为什么要另外套一层。 - 代理连接器:Tunnel 与 SOCKS:包着内层连接器的
Service<Uri>,先连代理再握手;目标缺端口一律按 443,200 之后早到的字节被丢掉,SOCKS5 用户名、密码、主机名合计不能超过 249 字节。 - client::pool:四块积木:
singleton、cache、negotiate、map四个 tower 积木,legacyClient不用;串行时能拼成 h1/h2 分流的池,并发时Singleton会 panic。
flowchart TB client["legacy Client:request"] --> pool["连接池:取或新建"] pool --> proxy["代理连接器:Tunnel / SOCKS"] proxy --> connector["HttpConnector:DNS 与 TCP"] pool --> connector connector --> handshake["conn::http1 handshake"] pool --> sendreq["SendRequest:请求通道"] handshake --> sendreq handshake --> conntask["Connection 任务:Dispatcher"] sendreq --> conntask conntask -->|"响应"| client
Connection 任务里跑的 Dispatcher 和服务端是同一个,见 HTTP/1 连接;HTTP/2 的客户端连接见 HTTP/2 客户端。
💬 关联讨论
发起讨论加载中...