HTTP之持久連接


HTTP/1.1 允許 HTTP 設備在事務處理結束之后將 TCP 連接保持在打開狀態,以便為未來的 HTTP 請求重用現存的連接。在事務處理結束后仍然保持在打開狀態的 TCP 連接被稱為持久連接。非持久連接會在每個事務結束之后關閉。持久連接會在不同事務之間保持打開狀態,直到客戶端或服務器決定將其關閉為止。

持久連接降低時延和連接建立的開銷,將連接保持在已調諧狀態,而且減少了打開連接的潛在數量。

持久連接與並行連接配合使用可能是最高效的方式。持久連接有兩種類型:比較老的 HTTP/1.0+ "keep-alive" 連接,以及現代的 HTTP/1.1 "persistent" 連接。

Keep-Alive 操作

實現 HTTP/1.0 keep-alive 連接的客戶端可以通過包含 Connection: Keep-Alive 首部請求將一條連接保持在打開狀態。

如果服務器願意為下一條請求將連接保持在打開狀態,就在響應中包含相同的首部。如果響應中沒有 Connection: Keep-Alive 首部,客戶端就認為服務器不支持 keep-alive,會在發回響應報文之后關閉連接。

Keep-Alive 選項

Keep-Alive 首部只是請求將連接保持在活躍狀態。發出 keep-alive 請求之后,客戶端和服務器並不一定會同意進行 keep-alive 會話。它們可以在任意時刻關閉空閑的 keep-alive 連接,並可隨意限制 keep-alive 連接所處理事務的數量。

可以用 Keep-Alive 通用首部中指定的、由逗號分隔的選項來調節 keep-alive 的行為:

  • 參數 timeout 是在 Keep-Alive 響應首部發送的。它估計了服務器希望將連接保持在活躍狀態的時間。這並不是一個承若值。
  • 參數 max 是在 Keep-Alive 響應首部發送的。它估計了服務器還希望為多少個事務保持此連接的活躍狀態。這並不是一個承若值。
  • Keep-Alive 首部還可支持未經處理的屬性,這些屬性主要用於診斷和調試。語法為 name [=value]。

Keep-Alive 首部完全是可選的,但只有在提供 Connection: Keep-Alive 時才能使用它。下面示例的 Keep-Alive 說明服務器最多還會為另外 5 個事務保持連接的打開狀態,或者將打開狀態保持到連接空閑了 2 分鍾之后。

Connection: Keep-Alive
Keep-Alive: max=5, timeout=120

Keep-Alive 連接的限制和規則

  • 在 HTTP/1.0 中,keep-alive 並不是默認使用的。客戶端必須發送一個 Connection: Keep-Alive 請求首部來激活 keep-alive 連接。
  • Connection: Keep-Alive 首部必須隨所有希望保持持久連接的報文一起發送。如果客戶端沒有發送 Connection: Keep-Alive 首部,服務器就會在那條請求之后關閉連接。
  • 客戶端探明響應中沒有 Connection: Keep-Alive 響應首部,就可以知道服務器發出響應之后是否會關閉連接了。
  • 只有在無需檢測到連接的關閉即可確定報文實體主體部分長度的情況下,才能將連接保持在打開狀態--也就是說實體的主體部分還必須有正確的 Content-Length,有多部件媒體類型,或者用分塊傳輸編碼的方式進行了編碼。在 keep-alive 信道中回送錯誤的 Content-Length 是很糟糕的事,這樣事務處理的另一端就無法精確地檢測到一條報文的結束和另一條報文的開始了。
  • 代理和網關必須執行 Connection 首部的規則。代理或網關必須在將報文轉發出去或將其高速緩存之前,刪除在 Connection 首部中命名的所有首部字段以及 Connection 首部自身。
  • 不應該與無法確定是否支持 Connection 首部的代理服務器建立 keep-alive 連接,以防止出現下面的啞代理問題。但在實際應用中不是總能做到這一點。
  • 從技術上講,應該忽略所有來自 HTTP/1.0 設備的 Connection 首部字段(包括 Connection: Keep-Alive),因為它們可能是由比較老的代理服務器誤轉發的。
  • 除非重復發送請求會產生其他一些副作用,否則如果在客戶端收到完整響應之前連接就關閉了,客戶端就一定要做好重試請求的准備。

Keep-Alive 和啞代理

1. Connection 首部和盲中繼

問題出在代理上--尤其是那些不理解 Connection 首部,而且不知道在沿着轉發鏈路將其發送出去之前,應該將該首部刪除的代理。很多老的或簡單的代理都是盲中繼(blind relay),它們只是將字節從一個連接轉發到另一個連接中去,不對 Connection 首部進行特殊的處理。

  • Web 向代理發送包含 Connection: Keep-Alive 首部的報文,期待建立一個 keep-alive 連接;
  • 啞代理收到這條請求,但並不理解 Connection 首部(只是將其作為一個擴展首部對待)。因此只是沿着轉發鏈路將報文一字不漏地發送給服務器。但 Connection 首部是個逐跳首部,只適用於單條傳輸鏈路,不應該沿着傳輸鏈路向下傳輸。
  • 經過中繼的 HTTP 請求到達 Web 服務器。當 Web 服務器收到經過代理轉發的 Connection: Keep-Alive 首部時,會誤以為代理希望進行 keep-alive 對話,因此回送一個 Connection: Keep-Alive 響應首部。所以,此時 Web 服務器認為它在與代理進行 keep-alive 對話,會遵循 keep-alive 規則。但代理卻對 keep-alive 一無所知。
  • 啞代理將 web 服務器的響應報文回送給客戶端,並將來自 web 服務器的 Connection: Keep-Alive 首部一起傳送過去。客戶端看到這個首部,會認為代理同意進行 keep-alive 對話。所以,此時客戶端和服務器都認為它們在進行 keep-alive 對話,但與它們進行對話的代理卻對 keep-alive 一無所知。
  • 由於代理不知道 keep-alive,所以會將收到的所有數據都回送給客戶端,然后等待源端服務器關閉連接。但源端服務器會認為代理已經顯式地請求它將連接保持在打開狀態了,所以不會去關閉連接。這樣,代理就會掛在那里等待連接的關閉。
  • 客戶端收到回送的響應報文時,會立即轉向下一條請求,在 keep-alive 連接上向代理發送另一條請求。而代理並不認為同一條請求上會有其他請求到來,請求被忽略,瀏覽器就掛在那里了。
  • 這種錯誤的通信方式會使瀏覽器一直處於掛起狀態,直到客戶端或服務器將來連接超時,並將其關閉為止。

代理和逐跳首部

為避免這類代理通信問題的發生,現代的代理都絕不能轉發 Connection 首部和所有名字出現在 Connection 值中的首部。因此,如果一個代理收到了一個 Connection: Keep-Alive 首部,是不應該轉發 Connection 首部,或所有名為 Keep-Alive 首部的。

如下幾個首部不能作為 Connection 首部值列出,也不能被代理轉發或作為緩存相應使用的首部:Proxy-Authenticate、Proxy-Connection、Transfer-Encoding 和 Upgrade。

插入 Proxy-Connection

對盲中繼的變通做法是引入了一個名為 Proxy-Connection 的新首部,解決了在客戶端后面緊跟着一個盲中繼所帶來的問題--但並沒有解決所有其他情況下存在的問題。

瀏覽器會向代理發送非標准的 Proxy-Connection 擴展首部,而不是官方支持的著名的 Connection 首部。如果代理是盲中繼,它會將無意義的 Proxy-Connection 首部轉發給 Web 服務器,服務器會忽略此首部,不會帶來任何問題。但如果是能夠理解持久連接的代理,就用一個 Connection 首部取代無意義的 Proxy-Connection 首部,然后將其發送給服務器。

Proxy-Connection 首部修正了單個盲中繼帶來的問題:

對有多層次代理的情況,Proxy-Connection 仍然無法解決問題:

HTTP/1.1 持久連接

HTTP/1.1 逐漸停止了對 keep-alive 連接的支持,用一種名為持久連接(persistent connection)的改進型設計取代了它。

HTTP/1.1 持久連接在默認情況下是激活的。除非特別指明,否則 HTTP/1.1 假定所有連接都是持久的。要在事務處理結束之后將連接關閉,HTTP/1.1 應用程序必須向報文中顯式地添加一個 Connection: close 首部。但是,客戶端和服務器仍然可以隨時關閉空閑的連接。不發送 Connection: close 並不意味着服務器承若永遠將連接保持在打開狀態。

持久連接的限制和規則

  • 發送了 Connection: close 請求首部之后,客戶端就無法在那條連接上發送更多的請求了。
  • 如果客戶端不想在連接上發送其他請求了,就應該在最后一條請求中發送一個 Connection: close 請求首部。
  • 只有當連接上所有的保衛都有正確的、自定義報文長度時--也就是說,實體主體部分的長度都和相應的 Connection-Length 一致,或者是用分塊傳輸編碼方式編碼的--連接才能持久連接。
  • HTTP/1.1 的代理必須能夠分別管理與客戶端和服務器的持久連接--每個持久連接都只適用於一跳傳輸。
  • HTTP/1.1 的代理服務器不應該與 HTTP/1.O 客戶端建立持久連接,除非它們了解客戶端的處理能力。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM