架構
Pingclair 支援 HTTP/1.1、HTTP/2 與 HTTP/3。各傳輸層負責處理協定資料,共用的政策層則負責路由、標頭規則、速率限制與存取日誌。本頁說明 v0.2.2 的組成元件、請求處理流程與協定差異。
🧱 伺服器是由幾個 crate 組成的單一二進位檔
Section titled “🧱 伺服器是由幾個 crate 組成的單一二進位檔”Pingclair 是一個 Cargo workspace。pingclair 二進位檔連結了下列 crate,每個 crate 只負責一件事。
| Crate | 職責 |
|---|---|
pingclair |
命令列進入點:參數解析、日誌、啟動、關閉與重載。 |
pingclair-config |
設定編譯器:讀取 Pingclairfile、檢查它,並產出伺服器實際執行的設定。 |
pingclair-proxy |
基於 Pingora 的 HTTP/1.1 與 HTTP/2、基於 quiche 的 HTTP/3、負載平衡,以及共用的請求政策層。 |
pingclair-static |
靜態檔案服務:讀檔、MIME 類型、range 與條件式請求,以及串流。 |
pingclair-fastcgi |
php_fastcgi 用來連到 PHP-FPM 的 FastCGI 用戶端。 |
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傳輸層轉接器把協定的 frame 組成一個請求,再往下交出去。路由、標頭規則、速率限制與存取日誌只在政策層實作一次,所以在 HTTP/1.1、HTTP/2 與 HTTP/3 上的行為都一樣。兩種傳輸層也透過同一個 connector 連到上游,因此連線池、上游 TLS 與逾時設定同樣是共用的。
🌊 每個請求都成立的事
Section titled “🌊 每個請求都成立的事”- 本文使用有界記憶體。 代理預設串流傳送;明確設定的請求或回應緩衝會先保留到設定上限,再串流傳送其餘資料。
unlimited緩衝仍有 8 MiB 上限。壓縮與快取仍有已知缺陷,部署前請先確認。 - 上游連線會重複使用。 連往後端的 keepalive 連線會放進連線池。以主機名稱指定的上游,會依
dns_refresh設定的間隔重新解析,所以後端容器重啟換了位址,也不需要人工介入就能跟上。 - 請求執行期間,設定只會被讀取,不會被修改。 每個請求讀的都是已發布的編譯後設定快照。重載會建立新的快照並替換上去;已經在執行的請求則在舊快照上完成。
🌐 各協定的差異
Section titled “🌐 各協定的差異”有幾項行為會因協定而不同。列在這裡,免得有人在正式環境中才發現。
| 領域 | v0.2.2 的行為 |
|---|---|
| Trailers | 任何協定都不轉送 request trailers。宣告了 trailers 的請求,會在回應開始前得到 501;若 HTTP/3 stream 的回應已經開始,則改為重設該 stream。上游回應若宣告了 trailers,會保留其狀態與本文,trailer 欄位則被丟棄。 |
CONNECT |
可用的 host:port 目標得到附帶 Allow 的 405,沒有可用連接埠的目標得到 400;HTTP/1.1 拒絕後關閉連線。 |
| FastCGI | php_fastcgi 在每種協定上都能運作,包括 HTTP/3。 |
📌 TRACE 同樣得到附帶 Allow 的 405。HTTP/1 格式錯誤的 chunked 本文、含原始空白或控制字元的目標,以及缺少 Host 的 HTTP/1.1 請求,皆得到 400 並關閉連線。
⚠️ 負載下 WebSocket 升級會間歇性失敗
Section titled “⚠️ 負載下 WebSocket 升級會間歇性失敗”Pingclair 可以代理 WebSocket,但機器忙碌時大約有 10–15% 的升級會失敗。從外部看,失敗的升級就是在 101 Switching Protocols 回應之後連線立刻被關閉。原因是上游 pingora-proxy crate 中的一個競態條件,而不是 Pingclair 的升級處理,也沒有任何設定能避開它。閒置的開發機很少能重現,所以特別在這裡說明。依據:CHANGELOG。
🧭 相關頁面
Section titled “🧭 相關頁面”📌 其他已知串流與協定缺陷請見專案狀態。HTTP/3 取消請求會釋放等待中的上游交換,且不影響同一連線的其他串流。
