HTTP/1.1 容許 HTTP 設備在事務處理結束以後將 TCP 鏈接保持在打開狀態,以便爲將來的 HTTP 請求重用現存的鏈接。在事務處理結束後仍然保持在打開狀態的 TCP 鏈接被稱爲持久鏈接。非持久鏈接會在每一個事務結束以後關閉。持久鏈接會在不一樣事務之間保持打開狀態,直到客戶端或服務器決定將其關閉爲止。web
持久鏈接下降時延和鏈接創建的開銷,將鏈接保持在已調諧狀態,並且減小了打開鏈接的潛在數量。瀏覽器
持久鏈接與並行鏈接配合使用多是最高效的方式。持久鏈接有兩種類型:比較老的 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 首部進行特殊的處理。
code
- 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 首部的。blog
以下幾個首部不能做爲 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 客戶端創建持久鏈接,除非它們瞭解客戶端的處理能力。