Rust in Everything
基于 pingora 0.9.0

源码地图

主线位置:三条主线经过的 crate 和它们之间的边。请求主线几乎全在 pingora-core 和 pingora-proxy 里,上游连接池单独在 pingora-pool;服务主线在 pingora-core 的 server、services 两个目录和 pingora-runtime 里;缓存主线从 pingora-proxy 的 proxy_cache.rs 进入 pingora-cache。本页说明这些 crate 谁依赖谁、每个从哪个文件读起、各有多大。

flowchart TB
  fac["pingora 门面"] --> proxy["pingora-proxy"]
  fac --> lb["load-balancing"]
  fac --> cache["pingora-cache"]
  fac --> core["pingora-core"]
  lb --> core
  lb --> ketama["pingora-ketama"]
  proxy --> cache
  proxy --> core
  cache --> core
  cache --> lru["pingora-lru"]
  cache --> hs["header-serde"]
  core --> pool["pingora-pool"]
  core --> rt["pingora-runtime"]
  core --> tls["pingora-openssl 等"]
  core --> http["pingora-http"]
  core --> tmo["pingora-timeout"]
  pool --> tmo
  http --> err["pingora-error"]
  mem["memory-cache"] --> ufo["TinyUFO"]
  lim["pingora-limits"]

图里只画了 pingora 自己的 crate 之间的主要依赖边,完整的边见下表。pingora-error 几乎被每个 crate 依赖,只画了一条;memory-cache 和 limits 两个是孤立的,门面 crate 不依赖它们。下面逐条核实。

依赖图:cargo tree 核实

依赖关系按实验项目 intro-run(模板的拷贝:pingora 开 openssl、proxy、lb、cache、time、connection_filter、upstream_modules)实际解析出来的看,命令是 cargo tree --offline -e normal -p <crate>@0.9.0 --depth 1,只取 pingora 自己的 crate:

crate直接依赖的 pingora crate值得一提的其他依赖
pingorapingora-core、pingora-http、pingora-timeout;pingora-proxy、pingora-cache、pingora-load-balancing 按 feature无
pingora-corepingora-error、pingora-http、pingora-pool、pingora-runtime、pingora-timeout;TLS 后端按 feature(本例 pingora-openssl)tokio、httparse、h2、socket2、nix、daemonix、brotli、flate2、zstd、clap、serde_yaml
pingora-proxypingora-core、pingora-cache、pingora-error、pingora-http
pingora-cachepingora-core、pingora-error、pingora-header-serde、pingora-http、pingora-lru、pingora-timeout
pingora-load-balancingpingora-core、pingora-error、pingora-http、pingora-ketama、pingora-runtimearc-swap
pingora-poolpingora-timeoutcrossbeam-queue(无锁的「热队列」)
pingora-httppingora-errorhttp
pingora-header-serdepingora-error、pingora-httpzstd
pingora-memory-cachepingora-error、pingora-timeout、TinyUFO
pingora-prometheuspingora-coreprometheus
pingora-error、pingora-timeout、pingora-runtime、pingora-limits、pingora-ketama、pingora-lru、TinyUFO无

从表里能读出四条:

  • 门面 crate 只转发。 pingora 的 lib.rs 全文 101 行,pub use pingora_core::* 之后是 http、cache、lb、proxy、time 五个模块别名和一个 prelude。读源码时直接去对应的 crate。

  • pingora-proxy 无条件依赖 pingora-cache。 pingora-proxy/Cargo.toml 里这一行没有 optional。只开 proxy 的 intro-notls 里反查:

    pingora-cache v0.9.0
    └── pingora-proxy v0.9.0
        └── pingora v0.9.0
            └── intro-notls v0.1.0 (~/exp-pingora/intro-notls)
    

    所以每个 pingora 代理都编译了缓存状态机,process_request 里的 proxy_cache 每个请求都会走一遍(不开缓存时只调 request_cache_filter 和 is_purge,实测见一次请求)。门面上的 cache feature 只决定有没有 pingora::cache 这个路径。

  • 没有 hyper。 cargo tree -p pingora@0.9.0 里一个 hyper 都没有:HTTP/1 的解析和编码是 pingora-core 在 httparse 之上自己写的,HTTP/2 直接用 h2。intro-run 里能看到 hyper,那是实验自己写上游和客户端用的。这一点和 hyper 卷 的对照放在 09 设计取舍。

  • 有两个 crate 门面不管。 pingora-limits(限流计数器)和 pingora-memory-cache(带读穿锁的内存缓存,底下是 TinyUFO)谁都不依赖它们,要用就自己在 Cargo.toml 里写,见 07 限流 和 08 TinyUFO 与内存缓存。pingora-lru 只被缓存的淘汰用,pingora-ketama 只被负载均衡的一致性哈希用。

整棵树(-p pingora@0.9.0,去重后)在模板的 feature 下是 171 个包,只开 proxy 时是 160 个。

📝NOTE
冷知识:Prometheus 在 0.9.0 才从 pingora-core 里搬出去

0.8.0 的 pingora-core/Cargo.toml 写着 prometheus = "0.13",没有 optional,Service::prometheus_http_service() 定义在 pingora-core/src/services/listening.rs 里,一个只想转发 HTTP 的代理也要编译整个 prometheus。0.9.0 里有两个提交改了它:5e5a374「make prometheus as optional dependency」(从 #612 同步过来)先把依赖改成 optional = true 并加了一个 prometheus feature;842ddd9(2026-04-01,「Split out pingora-prometheus into a separate crate」)再把 apps/prometheus_http_app.rs 和那个构造函数整个删掉,挪进新的 pingora-prometheus,现在的写法是 pingora_prometheus::prometheus_http_service()。两个提交都首次发布于 0.9.0,CHANGELOG 记在「Potential Breaking Changes」里:「Prometheus integration moved from pingora-core to the separate pingora-prometheus crate; Prometheus is optional.」所以上表里 pingora-prometheus 依赖 pingora-core,方向和 0.8.0 正好相反;intro-notls 里 cargo tree -i prometheus 报「did not match any packages」。

pingora-core 里的九个目录

pingora-core 的 src/ 有 47501 行,占全部 crate src/ 的一半多。lib.rs 声明了十个模块,其中 offload 是 pub(crate):

目录行数管什么主线讲它的页
server/3170Server、ServerConf 与 Opt、bootstrap、守护进程、平滑升级传 fd(transfer_fd/)服务配置与命令行、平滑升级与停机
services/1197Service<A>:监听服务的 accept 循环;后台服务服务、请求服务与监听器
listeners/2603Listeners、TransportStack:把地址和 TLS 设置变成可 accept 的栈;连接过滤请求服务与监听器
apps/654ServerApp、HttpServerApp:一条连接交给谁;HTTP/1 keep-alive 循环和 h2 分流请求一次请求
protocols/32619字节流之上的一切:l4/(2515)、tls/(2449)、http/v1/(13212)、http/v2/(3772)、http/subrequest/(3581)、http/compression/(2133)、digest.rs 等请求03 协议
connectors/4778连上游:TransportConnector(L4 + TLS + 池)、connectors/http/ 的 h1 / h2 会话请求L4 连接、HTTP 连接器
upstreams/937Peer trait、HttpPeer、PeerOptions:连哪里、怎么连请求HttpPeer
modules/574HttpModules:压缩、gRPC-Web 这类按请求装配的模块请求HTTP 模块与压缩
utils/731TLS 证书工具、BufRef 等杂项—用到时提

另有 lib.rs(131 行)和 offload.rs(107 行,TLS 握手和上游连接的卸载线程池)。protocols/ 一个目录就占了 pingora-core 的 69%,其中 HTTP/1 的服务端和客户端(http/v1/)比整个 pingora-proxy 的 src/ 还大。

每个 crate 从哪读起

行数是 tag 上 <crate>/src/ 下所有 .rs 文件的行数,含 #[cfg(test)] 模块,不含 tests/、benches/、examples/。卷目录(plans/)里的规模数字含 benches/ 和 examples/,所以 pingora-core 是 48168、pingora-proxy 是 13444,比下表大。

crate入口先读src/ 行数
pingorasrc/lib.rs:重新导出不用读101
pingora-coresrc/lib.rs:十个模块、按 feature 选 tlsserver/mod.rs、services/listening.rs、apps/mod.rs、protocols/l4/stream.rs、protocols/http/v1/server.rs、connectors/http/mod.rs47501
pingora-proxysrc/lib.rs:HttpProxy、Session、http_proxy_serviceproxy_trait.rs(ProxyHttp)、lib.rs 的 process_request、proxy_h1.rs、proxy_h2.rs、proxy_cache.rs、proxy_custom.rs11921
pingora-cachesrc/lib.rs:HttpCache、CachePhaselock.rs、key.rs、meta.rs、storage.rs、memory.rs、put.rs、eviction/13551
pingora-load-balancingsrc/lib.rs:Backend、Backends、LoadBalancerselection/、discovery.rs、health_check.rs、background.rs6596
pingora-poolsrc/lib.rsconnection.rs(ConnectionPool、PoolNode)、lru.rs1484
pingora-runtimesrc/lib.rs(只有这一个文件)RuntimeBuilder、Runtime::Steal / NoSteal782
pingora-timeoutsrc/lib.rs:timeout()fast_timeout.rs、timer.rs745
pingora-httpsrc/lib.rs:RequestHeader、ResponseHeadercase_header_name.rs、authority.rs2278
pingora-errorsrc/lib.rs:Error、ErrorType、ErrorSource、RetryTypeimmut_str.rs746
pingora-header-serdesrc/lib.rsdict.rs、thread_zstd.rs、trainer.rs673
pingora-lrusrc/lib.rs:分片 LRUlinked_list.rs、async_lru.rs、persistence.rs4303
pingora-memory-cachesrc/lib.rs:MemoryCacheread_through.rs(RTCache)1185
TinyUFO(目录 tinyufo)src/lib.rsbuckets.rs、estimation.rs1204
pingora-limitssrc/lib.rsrate.rs、estimator.rs、inflight.rs633
pingora-ketamasrc/lib.rs(只有这一个文件)609
TLS 后端pingora-openssl 294、pingora-boringssl 509、pingora-rustls 191、pingora-s2n 130只是对各 TLS 库的薄封装和扩展;TLS 的实现主体在 pingora-core/src/protocols/tls/

真正做事的代码集中在前四个 crate:core、proxy、cache、load-balancing 加起来 79569 行,是全部 22 个 crate src/(95981 行)的 83%。

上游文档对应本卷哪一页

仓库 docs/ 下有一篇 quick start 和 user guide 的 19 篇(含目录页 index.md)。它们是使用说明,本卷引用前先核实:

上游文档讲什么本卷
quick_start.md两个后端轮询的负载均衡器LoadBalancer
start_stop.md、conf.md、daemon.md命令行、配置文件、守护进程配置与命令行、一个 Server 的一生
graceful.md、systemd.md平滑升级与停机、systemd平滑升级与停机
internals.mdServer、服务、TransportStack、连接任务的关系图服务与监听器、一个 Server 的一生
panic.md请求里的 panic 不影响别的请求运行时;accept 循环里的 panic 不在此列,见怎么读这一卷 的 tokio 1.54 一节
phase.md、phase_chart.md、ctx.md、modify_filter.md钩子的顺序、CTX、改写请求ProxyHttp、一次请求
peer.mdHttpPeer 与 PeerOptionsHttpPeer
pooling.md上游连接池,哪些字段相同才算同一个 Peer连接池
errors.md、error_log.md、failover.md错误类型、日志级别、重试与换上游错误与重试
rate_limiter.mdpingora-limits 的 Rate限流
prom.mdpingora-prometheus不讲

index.md 的「Advanced topics (WIP)」下还列着「Pingora async runtime and threading model」「Background Service」「Blocking code in async context」等六个标题,都没有链接,还没写。线程模型见运行时,后台服务见一个 Server 的一生。

仓库里不讲的部分

  • pingora-boringssl、pingora-s2n:另两个 TLS 后端,本机没有它们的依赖缓存;讲 openssl 后端时对照一句。
  • pingora-prometheus(131 行):把 prometheus 的默认 registry 暴露成一个 HTTP 服务。
  • pingora-foundations(414 行,0.9.0 新增):接 Cloudflare 的 foundations 遥测库,MSRV 1.88,比工作区其他 crate 的 1.85 高;六个遥测 feature 一个都不开时 compile_error!。
  • pingora-core 里的 sentry、dial9 集成和 protocols/windows.rs。
  • pingora-proxy/src/subrequest/ 和 pingora-core/src/protocols/http/subrequest/:子请求只在缓存页里讲到为止。

小结

  • 依赖方向:门面 → proxy / lb / cache → core → pool、runtime、timeout、http、error;TLS 后端是 core 的可选依赖,同时只能有一个。
  • pingora-proxy 无条件依赖 pingora-cache,每个代理都编译、都走一遍 proxy_cache;门面的 cache feature 只加模块路径。
  • 依赖树里没有 hyper;HTTP/1 在 pingora-core/src/protocols/http/v1/(13212 行),HTTP/2 用 h2。
  • pingora-limits、pingora-memory-cache(加 TinyUFO)不在门面依赖里,要自己加。
  • pingora-core 47501 行,protocols/ 占 69%;核心四个 crate 占全部 src/ 的 83%。
  • Prometheus 在 0.9.0 从 core 里搬到 pingora-prometheus(5e5a374 / #612、842ddd9),依赖方向反了过来。

💬 关联讨论

发起讨论
加载中...