HTTP/1.x 中的連線管理
連線管理是 HTTP 中的關鍵主題:開啟和維護連線在很大程度上影響著網站和 Web 應用程式的效能。在 HTTP/1.x 中,有幾種模型:短連線、持久連線和HTTP 管道。
HTTP 主要依賴 TCP 作為其傳輸協議,在客戶端和伺服器之間提供連線。在 HTTP 的早期,它使用單一模型來處理此類連線。這些連線是短連線:每次需要傳送請求時都會建立一個新的連線,並在收到答案後關閉。
這種簡單模型在效能上存在一個內在的侷限性:開啟每個 TCP 連線都是一個資源消耗很大的操作。客戶端和伺服器之間必須交換多個訊息。網路延遲和頻寬會影響傳送請求時的效能。現代網頁需要許多請求(十幾個或更多)才能提供所需的資訊量,這證明了早期模型的低效性。
HTTP/1.1 中建立了兩種更新的模型。持久連線模型保持連線在連續請求之間保持開啟狀態,從而減少了開啟新連線所需的時間。HTTP 管道模型更進一步,透過傳送多個連續請求,甚至不等待答案,從而減少了網路中的大部分延遲。
注意:HTTP/2 為連線管理添加了額外的模型。
需要注意的是,HTTP 中的連線管理適用於兩個連續節點之間的連線,這是逐跳而不是端到端。客戶端與其第一個代理之間的連線所使用的模型可能與代理和目標伺服器之間的模型不同(或任何中間代理)。用於定義連線模型的 HTTP 標頭,如Connection和Keep-Alive,是逐跳標頭,其值可以被中間節點更改。
一個相關的主題是 HTTP 連線升級的概念,其中 HTTP/1.1 連線被升級到不同的協議,例如 TLS/1.0、WebSocket,甚至明文 HTTP/2。這種協議升級機制在其他地方有更詳細的說明。
短暫連線
HTTP 的原始模型,也是 HTTP/1.0 中的預設模型,是短連線。每個 HTTP 請求都在其自己的連線上完成;這意味著在每個 HTTP 請求之前都會發生 TCP 握手,並且這些握手是序列的。
TCP 握手本身很耗時,但 TCP 連線會適應其負載,在更持久的(或熱)連線下變得更高效。短連線不利用 TCP 的此效率特性,並且透過持續傳輸到新的、冷連線,效能會下降到最佳狀態。
這種模型是 HTTP/1.0 中使用的預設模型(如果沒有Connection標頭,或者其值為close)。在 HTTP/1.1 中,只有當Connection標頭髮送的值為close時,才會使用這種模型。
注意:除非處理非常舊的系統,該系統不支援持久連線,否則沒有令人信服的理由使用這種模型。
持久連線
短連線有兩個主要缺點:建立新連線所需的時間很長,並且底層 TCP 連線的效能只有在該連線使用了一段時間(熱連線)後才會變得更好。為了緩解這些問題,在 HTTP/1.1 之前就已經設計了持久連線的概念。或者,這也可以稱為保持活動連線。
持久連線是指在一段時間內保持開啟狀態的連線,可以重複使用於多個請求,從而避免了新的 TCP 握手,並利用了 TCP 的效能增強功能。此連線不會永遠保持開啟狀態:空閒連線會在一段時間後關閉(伺服器可以使用Keep-Alive標頭指定連線應保持開啟的最短時間)。
持久連線也有一些缺點;即使空閒時,它們也會消耗伺服器資源,並且在負載過重的情況下,可以進行DoS 攻擊。在這種情況下,使用非持久連線,即在空閒後立即關閉連線,可以提供更好的效能。
HTTP/1.0 連線預設情況下不是持久連線。將Connection設定為close以外的任何值,通常為retry-after,將使其持久化。
在 HTTP/1.1 中,永續性是預設設定,不再需要標頭(但通常會新增它作為針對需要回退到 HTTP/1.0 的情況的防禦措施)。
HTTP 管道
注意:HTTP 管道在現代瀏覽器中預設情況下未啟用。
- 有問題的代理仍然很常見,它們會導致奇怪且不穩定的行為,Web 開發人員無法輕易預測和診斷。
- 管道在正確實現方面很複雜:正在傳輸的資源的大小、將使用的有效RTT以及有效頻寬,對管道提供的改進具有直接影響。在不知道這些資訊的情況下,重要資訊可能會被延遲到不重要的資訊之後。甚至在頁面佈局期間,重要性的概念也在不斷發展!因此,HTTP 管道在大多數情況下僅帶來了微不足道的改進。
- 管道會受到HOL問題的困擾。
由於這些原因,管道已被一種更好的演算法——複用所取代,該演算法由 HTTP/2 使用。
預設情況下,HTTP請求是按順序發出的。只有在收到當前請求的響應後才會發出下一個請求。由於受到網路延遲和頻寬限制的影響,這可能會導致在下一個請求被伺服器看到之前出現明顯的延遲。
管道是透過同一個持久連線傳送連續請求,而不等待答案的過程。這避免了連線的延遲。理論上,如果將兩個 HTTP 請求打包到同一個 TCP 訊息中,也可以提高效能。典型的MSS(最大段大小)足夠大,可以包含多個簡單的請求,儘管 HTTP 請求的大小需求在不斷增長。
並非所有型別的 HTTP 請求都可以管道化:只有冪等方法,即GET、HEAD、PUT和DELETE,可以安全地重放。如果發生故障,可以重複管道內容。
如今,每個符合 HTTP/1.1 標準的代理和伺服器都應該支援管道化,儘管在實踐中許多代理和伺服器都有侷限性:這是現代瀏覽器預設情況下不啟用此功能的重要原因。
域名分片
注意:除非你有非常具體的迫切需要,否則不要使用這種已棄用的技術;改為切換到 HTTP/2。在 HTTP/2 中,域分片不再有用:HTTP/2 連線能夠很好地處理並行非優先順序請求。域分片甚至會損害效能。大多數 HTTP/2 實現使用一種名為連接合並的技術來恢復最終的域分片。
由於 HTTP/1.x 連線是序列化請求的,即使沒有任何排序,如果沒有足夠大的可用頻寬,它也不能達到最佳狀態。作為解決方案,瀏覽器對每個域開啟多個連線,傳送並行請求。預設值曾經是 2 到 3 個連線,但現在已經增加到更常見的 6 個並行連線。如果嘗試超過此數量,則存在觸發伺服器端DoS保護的風險。
如果伺服器希望網站或應用程式響應更快,則伺服器可以強制開啟更多連線。例如,與其將所有資源放在同一個域中,例如www.example.com,不如將其拆分為多個域,例如www1.example.com、www2.example.com、www3.example.com。這些域中的每一個都解析到同一個伺服器,並且 Web 瀏覽器將對每個域開啟 6 個連線(在我們的示例中,連線將增加到 18)。這種技術稱為域分片。
結論
改進的連線管理使 HTTP 的效能大幅提升。使用 HTTP/1.1 或 HTTP/1.0,使用持久連線——至少在連線變為空閒之前——可以帶來最佳效能。但是,管道化的失敗導致設計了更高階的連線管理模型,這些模型已被整合到 HTTP/2 中。