架构
🧱 组成组件
Section titled “🧱 组成组件”Pingclair 是一个 Cargo workspace。实际运行的服务器是 pingclair 二进制,它链接下列 crate。
| Crate | 职责 |
|---|---|
pingclair |
命令行入口:参数解析、日志、启动流程与服务封装。 |
pingclair-config |
配置编译器:词法分析、解析并检查 Pingclairfile 的语义。 |
pingclair-proxy |
基于 Pingora 的 HTTP/1.1 与 HTTP/2 代理、基于 quiche 的 HTTP/3 listener、负载均衡,以及共用的请求策略层。 |
pingclair-static |
静态文件服务:文件读取、MIME 类型、范围请求与流式传输。 |
pingclair-tls |
证书管理:手动证书、常驻的内部证书颁发机构,以及自动 ACME 签发。 |
pingclair-api |
用于查看状态并重新加载配置的 Admin API。 |
pingclair-core |
上述 crate 共用的数据结构与服务器生命周期。 |
🚦 请求的路径
Section titled “🚦 请求的路径”client | | TLS with ALPN, or QUIC vlistener HTTP/1.1 and HTTP/2 on TCP, HTTP/3 on UDP | vtransport adapter Pingora ProxyHttp for TCP, tokio-quiche for QUIC | vpolicy layer routing, matchers, headers, rate limits, access log | vhandler file server | reverse proxy | FastCGI | static response | vupstream or disk两种传输最终都汇入同一个策略层,因此路由、请求头处理、限流与访问日志在 HTTP/1.1、HTTP/2、HTTP/3 上的行为一致。只有在协议本身要求不同的时候,两种传输才会出现差异。
🌊 请求处理特性
Section titled “🌊 请求处理特性”- Body 以流式处理。 请求与响应的 body 以有界块流经代理。压缩、中间件与代理都不会缓冲完整的 body,因此大文件上传或缓慢的读取端不会占用与 body 大小成正比的内存。
- 上游连接会被复用。 与后端之间的 keepalive 连接会被复用。主机名上游会按
dns_refresh设定的间隔重新解析,因此重启并拿到新地址的容器不需要人工介入就能跟上。 - 运行时状态在请求期间不可变。 请求读取的是已发布的 snapshot;重新加载会发布新的 snapshot,而不是修改正在使用的那一份。
🌐 各协议的差异
Section titled “🌐 各协议的差异”部分行为因协议而异,这是刻意的设计。这里先列出,而不是让使用者事后才发现:
| 领域 | 行为 |
|---|---|
| Trailers | 请求中声明的 trailer 不会被转发。服务器会在响应提交前返回 501、对已提交的 HTTP/3 流发出 reset,并在上游声明响应 trailer 时返回 502。 |
| CONNECT | 在 HTTP/3 上,CONNECT 与 extended CONNECT 会返回 501,直到 tunnel 支持被实现。 |
| FastCGI | php_fastcgi 支持 HTTP/1.1 与 HTTP/2。需要 FastCGI 的路由在 HTTP/3 上会返回 501,直到该路径拥有自己的 FastCGI 客户端。 |
⚠️ 已知缺陷
Section titled “⚠️ 已知缺陷”WebSocket 升级在负载下会间歇性失败:在繁忙的机器上约有 10-15% 的升级失败。原因是上游 pingora-proxy crate 的竞态,而不是 Pingclair 自己的升级处理;并且在空闲的开发机上完全看不到,所以必须写在这里。上游 issue:cloudflare/pingora#946。
