Rust in Everything
基于 pingora 0.9.0

pingora 源码解析

pingora 是 Cloudflare 开源的 Rust 框架,用来写可编程的 HTTP 代理。它跑在 tokio 上,但不用 hyper:HTTP/1 是自己在 httparse 之上实现的,HTTP/2 用 h2,TLS 有 openssl、boringssl、rustls、s2n 四个后端可选。写一个代理,就是实现 ProxyHttp 这个 trait:必须写的只有 new_ctx 和 upstream_peer(请求发往哪里),其余四十个方法都有默认实现(其中两个要开 upstream_modules feature 才有),在请求的固定阶段被调用。仓库 README 的说法是「a Rust framework to build fast, reliable and programmable networked systems」。这句话落在哪些代码上,和 nginx 那样的配置驱动代理、用 hyper 自己搭的代理有什么不同,见 pingora 解决什么问题。

锁定的版本

项本卷说明
版本0.9.0工作区 22 个 crate 同一个版本号,一起发版
tag0.9.0 → 702f690(2026-09-09,「Update changelog for 0.9.0」)crates.io 上各包 .cargo_vcs_info.json 记的就是这个 commit;CHANGELOG 给 0.9.0 写的日期是 2026-09-04
历史首个提交 2023-05-08,到 tag 共 764 个提交;0.1.0 在 CHANGELOG 里记为 2024-02-28(tag 指向的 b4264f1 是 2024-04-05)仓库从 Cloudflare 内部仓库同步,大多数提交不带 PR 号
tag 之后main 到 84f21ee(2026-10-22)又有 36 个提交,没有发版本卷不跟;某页讲的代码在这之后改过,会提一句
tokio实验一律钉在 1.53.2tokio 1.54 让 pingora 0.9.0 接受第一个连接就 panic,原因和修复见怎么读这一卷

源码链接一律指向 tag,形如 HttpProxy::process_request。

讲什么

  • pingora-core:Server 与服务、监听与 L4 流、HTTP/1 服务端和客户端、HTTP/2、TLS 抽象、上游连接器与 HttpPeer、HTTP 模块。
  • pingora-proxy:ProxyHttp 的阶段与钩子、HttpProxy::process_request、h1 / h2 双向转发、错误与重试、上游模块。
  • pingora-cache:缓存状态机、缓存键与元数据、缓存锁、存储与淘汰。
  • 负载均衡与限流:pingora-load-balancing、pingora-ketama、pingora-limits。
  • 基础组件:pingora-pool、pingora-timeout、pingora-memory-cache 与 TinyUFO、pingora-lru、pingora-http、pingora-error、pingora-runtime、pingora-header-serde。
  • 不讲:boringssl 和 s2n 后端(openssl 后端讲到的地方对照一句)、pingora-prometheus、pingora-foundations、sentry 与 dial9 集成、Windows 支持。

三条主线

全卷沿三条主线展开,每页开头标出自己在哪条线上:

  • 请求主线(一次 curl http://127.0.0.1:<端口>/):监听器 accept,TransportStack 建 Stream、set_buffer → HttpServerApp::process_new_http,HTTP/1 服务端读请求头 → HttpProxy::process_request 依次调 early_request_filter、request_filter、upstream_peer → 连接器从池里取上游连接或新建 → upstream_request_filter → h1 双向转发请求体和响应体,中间经过 upstream_response_filter、response_filter、response_body_filter → logging → 下游连接回到 keep-alive 循环,上游连接回池。
  • 服务主线(Server 的一生):Server::new 读选项和配置 → bootstrap(升级时从旧进程接收监听 fd)→ add_service → run_forever:每个服务一个运行时、起监听 → 等信号:SIGQUIT 平滑升级、SIGTERM 平滑停机、SIGINT 立刻退出 → 停机流程。
  • 缓存主线(同一个 URL 请求两次):request_cache_filter 打开缓存 → cache_key_callback → 查找,miss → 缓存锁 → 上游响应 → response_cache_filter 判断能否缓存 → 写入存储 → 第二次请求 hit,cache_hit_filter,不连上游。

每条线的每一步落在哪个函数、哪一页讲,见怎么读这一卷。

flowchart TB
  intro[00 导读] --> overview[01 全景]
  overview --> server[02 服务器]
  overview --> proto[03 协议]
  proto --> conn[04 连接上游]
  conn --> proxy[05 代理]
  server --> proxy
  proxy --> cache[06 缓存]
  proxy --> lb["07 负载均衡与限流"]
  cache --> comp[08 基础组件]
  lb --> comp
  comp --> design[09 设计取舍]
  design --> pit[10 常见坑]
  pit --> site[11 在本站中]
  site --> hands[12 动手]

实验环境

关于行为的说法(钩子按什么顺序调、上游连接有没有复用、SIGTERM 之后先停什么、报什么错)都配一个小程序和它的真实输出。平台是 M2 Max、macOS 27、rustc 1.99.0,tokio 1.53.2。没有 Linux 机器可用,而 pingora 的 README 写明「Linux is our tier 1 environment」:平滑升级传 fd、一部分 socket 选项只在 #[cfg(target_os = "linux")] 下编译,这些地方只读源码,页面上写明。具体的模板、feature 和端口约定见怎么读这一卷。

各部分

  1. 00 导读:锁定版本;三条主线每一步落在哪;实验环境与 tokio 1.53.2 的原因;feature 表与它们之间的约束;crate 依赖图、各 crate 入口和规模。
  2. 01 全景:pingora 在代理的几种写法里的位置;一次请求经过的全部钩子(实测顺序和上游端口);一个 Server 从启动到 SIGTERM 退出(实测线程名和停机顺序)。
  3. 02 服务器:配置与命令行、服务与监听器、运行时、平滑升级与停机。
  4. 03 协议:L4 流、HTTP/1 服务端、HTTP/1 的 body、HTTP/2、TLS、HTTP 模块与压缩。
  5. 04 连接上游:HttpPeer、L4 连接、连接池、HTTP 连接器。
  6. 05 代理:ProxyHttp、process_request、双向转发、错误与重试、上游模块。
  7. 06 缓存:缓存状态机、键与元数据、缓存锁、存储与淘汰。
  8. 07 负载均衡与限流:LoadBalancer、选择算法、健康检查、限流。
  9. 08 基础组件:TinyUFO 与内存缓存、超时、请求头与错误。
  10. 09 设计取舍:自己写 HTTP/1、固定阶段的钩子、每服务一个运行时、跨连接的连接池、四选一的 TLS 后端,和 hyper、tower、tokio 对照。
  11. 10 常见坑:报错原文做标题,每个都先跑出最小复现。
  12. 11 在本站中:本站网关(pingora 0.8.0)的每一处调到 pingora 的哪段源码,升级到 0.9.0 会碰到什么。
  13. 12 动手:不依赖 pingora,约 500 行写一个带钩子和上游连接池的 mini 代理。

适合谁读

  • 用过 nginx、想知道「用代码写代理」是什么样子的读者:从导读开始,再读 01 全景 的三页,然后是 05 代理。
  • 读过 hyper 卷、想看同样在 tokio 上的另一套 HTTP 实现的读者:先读一次请求,再读 03 协议 和 04 连接上游,最后读 09 设计取舍。
  • 读过 tower 卷 的读者:tower 的中间件是 Service 套 Service,pingora 是一个 trait 上的固定阶段,对照从 ProxyHttp 开始。
  • 需要的前置知识:async Rust 与 tokio 的运行时、async_trait;HTTP/1.1 的 keep-alive、Content-Length 与 chunked;知道反向代理里「下游」(downstream,客户端一侧)和「上游」(upstream,后端服务一侧)指什么。

💬 关联讨论

发起讨论
加载中...