连接上游
请求主线走到 upstream_peer 时,用户返回一个 HttpPeer。从这里到请求头真正写上上游 socket,中间的事都在这一部分:pingora-core 的 upstreams/peer.rs(920 行)、connectors/(mod.rs、l4.rs 和 http/ 下四个文件共 3600 行,不算 tls/ 下各后端),以及独立的 pingora-pool crate(src/ 下 1484 行,含单元测试)。
这一部分回答四个问题:连谁、用什么参数连(peer);一条新连接怎么建起来(L4 和 TLS);用完的连接放在哪、怎么知道它死了(连接池);HTTP/1 和 HTTP/2 怎么分别复用(HTTP 连接器)。
- Peer:
Peertrait 只有四个必须实现的方法,其余从PeerOptions读;HttpPeer::reuse_hash哈希地址、scheme、SNI、证书校验相关的字段、group_key、max_h2_streams,0.9.0 加上了curves和second_keyshare;ALPN、各种超时和每个 peer 自己的 CA 不在里面,实测确认。 - L4 连接:
HttpPeer::new在调用线程上同步解析域名,解析失败直接 panic,实测一次慢解析把同一线程上的其他请求一起拖住;connection_timeout分别限制 TCP 连接和 TLS 握手,total_connection_timeout限制整段;TCP_FASTOPEN、IP_BIND_ADDRESS_NO_PORT、DSCP 只在 Linux 生效。 - 连接池:
ConnectionPool是DashMap分组加一个 16 分片、带全局序号的 LRU,0.9.0 起替换了按线程各自一份的ThreadLocalLRU;每条空闲连接有一个idle_poll任务盯着;上游关掉空闲连接后下一个请求开新连接,关在请求到达那一刻时 GET 被重试、POST 回 502,都实测过。 - HTTP 连接器:
get_http_session按 ALPN 决定查哪个池;h1 连接整条复用,h2 连接按max_h2_streams(默认 1)分流;实测并发请求下 h2 复用有「假阴性」,ALPN 设成只要 h2 而上游不支持时每个请求都新建连接。
flowchart TB peer["upstream_peer:HttpPeer"] --> getsess["get_http_session"] getsess --> h1c["v1::Connector"] getsess --> h2c["v2::Connector"] h1c --> tc1["TransportConnector"] h2c --> tc2["TransportConnector"] h2c --> h2pool["in_use / idle 池"] tc1 --> pool["ConnectionPool"] tc1 --> l4["l4::connect"] tc2 --> l4 l4 --> tls["tls::connect"] getsess --> release["release_http_session"] release --> pool release --> h2pool
HttpProxy 只持有一个 http::Connector,所有下游连接、所有工作线程共用它,所以上游连接池是整个服务一份,不跟着下游连接走。连接建好以后请求怎么写、响应怎么读,是协议那一部分的事;连接失败或复用的连接中途断开之后要不要重试、重试几次,在 错误、重试与故障转移。和 hyper-util 的连接池、HttpConnector 对照,见 hyper 卷的客户端。
💬 关联讨论
发起讨论加载中...