# 🏗️ 架構 Pingclair 支援 HTTP/1.1、HTTP/2 與 HTTP/3。各傳輸層負責處理協定資料,共用的政策層則負責路由、標頭規則、速率限制與存取日誌。本頁說明 **v0.2.2** 的組成元件、請求處理流程與協定差異。 ## 🧱 伺服器是由幾個 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 共用的資料結構與生命週期。 | ## 🚦 每個請求都經過同一個政策層 ```text client | | TLS with ALPN, or QUIC v listener HTTP/1.1 and HTTP/2 on TCP, HTTP/3 on UDP | v transport adapter Pingora ProxyHttp for TCP, tokio-quiche for QUIC | v policy layer routing, matchers, headers, rate limits, access log | v handler file server | reverse proxy | FastCGI | static response | v upstream or disk ``` 傳輸層轉接器把協定的 frame 組成一個請求,再往下交出去。路由、標頭規則、速率限制與存取日誌只在政策層實作一次,所以在 HTTP/1.1、HTTP/2 與 HTTP/3 上的行為都一樣。兩種傳輸層也透過同一個 connector 連到上游,因此連線池、上游 TLS 與逾時設定同樣是共用的。 ## 🌊 每個請求都成立的事 - **本文使用有界記憶體。** 代理預設串流傳送;明確設定的請求或回應緩衝會先保留到設定上限,再串流傳送其餘資料。`unlimited` 緩衝仍有 8 MiB 上限。壓縮與快取仍有[已知缺陷](/zh-TW/project/status/),部署前請先確認。 - **上游連線會重複使用。** 連往後端的 keepalive 連線會放進連線池。以主機名稱指定的上游,會依 `dns_refresh` 設定的間隔重新解析,所以後端容器重啟換了位址,也不需要人工介入就能跟上。 - **請求執行期間,設定只會被讀取,不會被修改。** 每個請求讀的都是已發布的編譯後設定快照。重載會建立新的快照並替換上去;已經在執行的請求則在舊快照上完成。 ## 🌐 各協定的差異 有幾項行為會因協定而不同。列在這裡,免得有人在正式環境中才發現。 | 領域 | 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 升級會間歇性失敗 Pingclair 可以代理 WebSocket,但機器忙碌時大約有 10–15% 的升級會失敗。從外部看,失敗的升級就是在 `101 Switching Protocols` 回應之後連線立刻被關閉。原因是上游 `pingora-proxy` crate 中的一個競態條件,而不是 Pingclair 的升級處理,也沒有任何設定能避開它。閒置的開發機很少能重現,所以特別在這裡說明。依據:[CHANGELOG](https://github.com/dorianverlaine/pingclair/blob/main/CHANGELOG.md)。 ## 🧭 相關頁面 - [設定模型](/zh-TW/concepts/configuration/):Pingclairfile 如何變成上面描述的快照。 - [專案狀態](/zh-TW/project/status/):這個發行版支援與拒絕的項目。 📌 其他已知串流與協定缺陷請見[專案狀態](/zh-TW/project/status/)。HTTP/3 取消請求會釋放等待中的上游交換,且不影響同一連線的其他串流。