題目與背景
HTTP/3 閘道承載長連線和多路並行請求。發布新版本時,希望拒絕新請求、讓已接受請求完成,並在逾時後安全關閉。請基於 RFC 9114 設計 GOAWAY 下線流程,說明它與 QUIC 關閉及 HTTP 重試的邊界。
面試官考察什麼
重點是區分 GOAWAY 的「限制後續請求」與 CONNECTION_CLOSE 的「終止 QUIC 連線」。HTTP/3 GOAWAY 帶有請求串流 ID 上界,伺服器可先送較大值再遞減到最終邊界。還要涵蓋並行、客戶端判斷未處理請求、冪等性、代理、逾時與觀測。
先問清楚的澄清問題
請求與連線形態
確認是否有長輪詢、上傳、WebTransport 或非冪等 POST,以及是否經過反向代理。不同串流的完成時間與重試風險不同。
下線目標與逾時
釐清是連線排空、節點維護或故障隔離,設定最大排空與硬關閉時間及發布批次。沒有上限會讓舊連線無限占用資源。
用戶端能力
確認用戶端和代理是否正確處理 GOAWAY、是否支援遷移與重連,以及能否區分「伺服器拒絕新串流」和「請求已執行但回應遺失」。
30 秒回答框架
「HTTP/3 GOAWAY 宣告請求串流 ID 上界,收到後不能建立超過邊界的新請求。伺服器先送較寬的 GOAWAY 觀察並行請求,再遞減到最終邊界,讓用戶端知道哪些請求可能未處理。排空期間保留已接受串流,逾時後以 QUIC CONNECTION_CLOSE 結束。用戶端只在確認請求未執行且可安全重試時重試。」
深入解答步驟
第一步:區分 GOAWAY 與 CONNECTION_CLOSE
GOAWAY 是 HTTP/3 控制訊息,限制請求串流最大 ID,不會立即終止連線。CONNECTION_CLOSE 會關閉 QUIC,可能中斷執行中的請求。下線先排空,硬逾時才關閉。
第二步:採用先寬後窄的邊界
先送較大 ID 覆蓋已觀察到的並行請求,停止新工作後再送較小的最終 ID。用戶端不能把遞減過程視為允許新請求的訊號,實作要保存控制訊息順序和狀態。
第三步:處理已接受、未開始與未知請求
低於最終邊界且已建立的串流繼續完成;高於邊界的請求視為未接受,可在新連線重試。若無法判斷非冪等請求是否執行,應用需要冪等鍵或查詢路徑,不能盲目重放。
第四步:設計代理與連線遷移
反向代理收到上游 GOAWAY 後應停止向該連線分配新請求並傳播狀態。QUIC 連線遷移不等於請求遷移;不能假定換路徑即可安全轉移執行中的串流。
第五步:設定排空與硬關閉
記錄 GOAWAY 時間、最後活動串流和剩餘請求。排空逾時後取消仍未完成串流,硬關閉時間到才用 CONNECTION_CLOSE 釋放連線。控制器要限制同時下線實例數。
第六步:保護重試與副作用
只對確認未執行的冪等方法或帶冪等鍵的寫操作重試,使用退避與預算。回應遺失不代表請求未執行;付款或扣庫存等副作用必須由伺服器去重。
第七步:驗證發布效果
灰度注入長請求、並行串流、重連、GOAWAY 遞減、代理轉發和硬關閉。監控新串流拒絕、排空時間、未完成串流、重試率、重複副作用、連線錯誤和容量餘量。
高品質示例回答
我會先停止新連線分配,再送寬範圍 GOAWAY 讓用戶端停止建立新串流;確認並行請求後送更小的最終邊界。低於邊界的已接受串流完成,逾時才取消並以 CONNECTION_CLOSE 釋放連線。用戶端只對確認未執行且冪等的請求重試,非冪等寫操作依賴冪等鍵。代理需傳播狀態,連線遷移不能當作請求遷移。
常見錯誤
- 錯誤: 發送 GOAWAY 就立即關閉 QUIC。→ 原因: 執行中的請求會被中斷。→ 改進: 先排空,硬逾時才關閉。
- 錯誤: 把 GOAWAY 上界當成已執行清單。→ 原因: 串流 ID 只表示範圍。→ 改進: 用應用狀態和冪等鍵確認結果。
- 錯誤: 所有失敗請求都自動重試。→ 原因: 回應遺失可能伴隨副作用。→ 改進: 限定冪等方法或帶鍵寫入。
- 錯誤: 連線遷移可以轉移執行中的請求。→ 原因: 路徑變化不改變 HTTP 串流狀態。→ 改進: 透過新連線安全重試。
追問與回答
追問 1:為什麼 GOAWAY 可以遞減?
伺服器先覆蓋已觀察到的並行請求,再確定最終不接受的新串流邊界。遞減讓用戶端逐步收斂到安全邊界。
追問 2:用戶端如何判斷請求是否被接受?
比較串流 ID 與最終邊界只能判斷可能範圍,不能證明應用是否執行,還要結合回應、連線錯誤與冪等查詢。
追問 3:GOAWAY 會自動跨代理傳播嗎?
不應假定。代理是獨立端點,需要把上游排空狀態轉換為下游連線和路由策略。
追問 4:硬關閉前為何還要取消串流?
先取消可釋放應用資源並記錄原因,讓用戶端區分排空逾時與網路故障;最後關閉 QUIC 才是資源邊界。