代理
这一部分讲 pingora-proxy 0.9.0(tag 0.9.0)和它用的 pingora-error。pingora-proxy/src/ 一共 11921 行:lib.rs(2630 行,HttpProxy、Session、process_request)、proxy_trait.rs(757 行,ProxyHttp)、proxy_h1.rs、proxy_h2.rs、proxy_custom.rs(三种上游的双向转发,合计 3960 行)、proxy_common.rs(状态机和请求头清理)、proxy_cache.rs(2791 行,第 06 部分讲)、proxy_purge.rs 和 subrequest/。算上 examples/ 和 benches/、不算 tests/ 是 13444 行。pingora-error 675 行。
前面几部分讲的都是零件:服务器怎么跑起来、连接怎么读写、上游连接从哪来。这一部分把它们串起来:一个请求进来之后,框架在哪些点调用用户的代码,数据怎么在两条连接之间流动,出错时怎么决定重试还是放弃。
- ProxyHttp:trait 有 42 个方法,必须实现的只有
new_ctx和upstream_peer,17 个async;按阶段分组列出默认实现,对照phase_chart.md找出图上缺的和画错的;request_filter的Ok(true)是「已回应」,只返回不写会让客户端一直等;响应体钩子是同步的,靠返回Duration限速。 - process_request:从
process_new_http到finish逐段走;读请求头时和停机通知select!,0.9.0 把停机通知按线程分片;畸形请求头和CONNECT在任何钩子之前就被拒,logging看不到;缓存分支只简述;重试循环每轮都从upstream_peer开始。 - 双向转发:h1 转发是
try_join!里的两个select!循环加两个容量 4 的HttpTask管道;先预留管道空位再读下游以免死锁;实测上游慢读时背压稳定在约 1.1 MB;0.8.0 的响应头读取不是 cancel safe 的,复现成 502;proxy task 只用于从缓存出响应;默认只转发 WebSocket 的 upgrade。 - 错误、重试与故障转移:
Error的类型、来源、重试标记和原因链;连接失败默认不重试;max_retries是总尝试次数,设成 0 一次都不试;0.9.0 起复用连接上失败的 POST 不再重试,0.8.0 会让上游收到两次;fail_to_proxy的状态码规则。 - 上游模块与自定义转发:0.9.0 的上游模块在解压之前看响应,只有响应方向;模块
order()取负在i16::MIN上溢出;proxy_custom的自定义消息转发;connection_filter在 accept 循环里串行执行,和 0.9.0 的 pre-TLS 回调不是一回事。
flowchart TB accept["accept 与 connection_filter"] --> pnh[process_new_http] pnh --> pr[process_request] pr --> hooks["ProxyHttp 钩子"] pr --> retry[重试循环] retry --> conn[取上游连接] conn --> h1[proxy_h1] conn --> h2[proxy_h2] conn --> custom[proxy_custom] h1 --> tasks[HttpTask 管道] h2 --> tasks custom --> tasks tasks --> mods[上游与下游模块] retry --> err[pingora-error]
第一页是接口,第二页是骨架,第三页是骨架里最长的一段,第四页是失败的那条岔路,第五页是 0.9.0 的新东西和两个边缘机制。上游连接本身(HttpPeer、连接池、h1/h2 连接器)见 连接上游;缓存分支见 缓存状态机。和 hyper 的对照:hyper 只提供连接级别的 HTTP 实现,代理逻辑(转发、重试、改头)要用户自己写在 Service 里,见 hyper 卷的 dispatcher;pingora 把这部分做成了框架,用户只填钩子。
💬 关联讨论
发起讨论加载中...