源码地图
主线位置:三条主线经过的 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 | 值得一提的其他依赖 |
|---|---|---|
pingora | pingora-core、pingora-http、pingora-timeout;pingora-proxy、pingora-cache、pingora-load-balancing 按 feature | 无 |
pingora-core | pingora-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-proxy | pingora-core、pingora-cache、pingora-error、pingora-http | |
pingora-cache | pingora-core、pingora-error、pingora-header-serde、pingora-http、pingora-lru、pingora-timeout | |
pingora-load-balancing | pingora-core、pingora-error、pingora-http、pingora-ketama、pingora-runtime | arc-swap |
pingora-pool | pingora-timeout | crossbeam-queue(无锁的「热队列」) |
pingora-http | pingora-error | http |
pingora-header-serde | pingora-error、pingora-http | zstd |
pingora-memory-cache | pingora-error、pingora-timeout、TinyUFO | |
pingora-prometheus | pingora-core | prometheus |
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,实测见一次请求)。门面上的cachefeature 只决定有没有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 个。
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/ | 3170 | Server、ServerConf 与 Opt、bootstrap、守护进程、平滑升级传 fd(transfer_fd/) | 服务 | 配置与命令行、平滑升级与停机 |
services/ | 1197 | Service<A>:监听服务的 accept 循环;后台服务 | 服务、请求 | 服务与监听器 |
listeners/ | 2603 | Listeners、TransportStack:把地址和 TLS 设置变成可 accept 的栈;连接过滤 | 请求 | 服务与监听器 |
apps/ | 654 | ServerApp、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/ | 937 | Peer trait、HttpPeer、PeerOptions:连哪里、怎么连 | 请求 | HttpPeer |
modules/ | 574 | HttpModules:压缩、gRPC-Web 这类按请求装配的模块 | 请求 | HTTP 模块与压缩 |
utils/ | 731 | TLS 证书工具、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/ 行数 |
|---|---|---|---|
pingora | src/lib.rs:重新导出 | 不用读 | 101 |
pingora-core | src/lib.rs:十个模块、按 feature 选 tls | server/mod.rs、services/listening.rs、apps/mod.rs、protocols/l4/stream.rs、protocols/http/v1/server.rs、connectors/http/mod.rs | 47501 |
pingora-proxy | src/lib.rs:HttpProxy、Session、http_proxy_service | proxy_trait.rs(ProxyHttp)、lib.rs 的 process_request、proxy_h1.rs、proxy_h2.rs、proxy_cache.rs、proxy_custom.rs | 11921 |
pingora-cache | src/lib.rs:HttpCache、CachePhase | lock.rs、key.rs、meta.rs、storage.rs、memory.rs、put.rs、eviction/ | 13551 |
pingora-load-balancing | src/lib.rs:Backend、Backends、LoadBalancer | selection/、discovery.rs、health_check.rs、background.rs | 6596 |
pingora-pool | src/lib.rs | connection.rs(ConnectionPool、PoolNode)、lru.rs | 1484 |
pingora-runtime | src/lib.rs(只有这一个文件) | RuntimeBuilder、Runtime::Steal / NoSteal | 782 |
pingora-timeout | src/lib.rs:timeout() | fast_timeout.rs、timer.rs | 745 |
pingora-http | src/lib.rs:RequestHeader、ResponseHeader | case_header_name.rs、authority.rs | 2278 |
pingora-error | src/lib.rs:Error、ErrorType、ErrorSource、RetryType | immut_str.rs | 746 |
pingora-header-serde | src/lib.rs | dict.rs、thread_zstd.rs、trainer.rs | 673 |
pingora-lru | src/lib.rs:分片 LRU | linked_list.rs、async_lru.rs、persistence.rs | 4303 |
pingora-memory-cache | src/lib.rs:MemoryCache | read_through.rs(RTCache) | 1185 |
TinyUFO(目录 tinyufo) | src/lib.rs | buckets.rs、estimation.rs | 1204 |
pingora-limits | src/lib.rs | rate.rs、estimator.rs、inflight.rs | 633 |
pingora-ketama | src/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.md | Server、服务、TransportStack、连接任务的关系图 | 服务与监听器、一个 Server 的一生 |
panic.md | 请求里的 panic 不影响别的请求 | 运行时;accept 循环里的 panic 不在此列,见怎么读这一卷 的 tokio 1.54 一节 |
phase.md、phase_chart.md、ctx.md、modify_filter.md | 钩子的顺序、CTX、改写请求 | ProxyHttp、一次请求 |
peer.md | HttpPeer 与 PeerOptions | HttpPeer |
pooling.md | 上游连接池,哪些字段相同才算同一个 Peer | 连接池 |
errors.md、error_log.md、failover.md | 错误类型、日志级别、重试与换上游 | 错误与重试 |
rate_limiter.md | pingora-limits 的 Rate | 限流 |
prom.md | pingora-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;门面的cachefeature 只加模块路径。- 依赖树里没有 hyper;HTTP/1 在
pingora-core/src/protocols/http/v1/(13212 行),HTTP/2 用h2。 pingora-limits、pingora-memory-cache(加TinyUFO)不在门面依赖里,要自己加。pingora-core47501 行,protocols/占 69%;核心四个 crate 占全部src/的 83%。- Prometheus 在 0.9.0 从 core 里搬到
pingora-prometheus(5e5a374/ #612、842ddd9),依赖方向反了过来。