一、HTTP/1.0
HTTP1.0版本的Keep-alive並不像HTTP1.1那樣是默認發送的,所以要想連接得到保持,必須手動配置發送connection:keep-alive字段。若想斷開keep-alive連接,需發送Connection:close字段
注意:這里的連接是HTTP依賴的傳輸協議TCP,而不是HTTP本身。
為什么需要長連接?
長連接可以提高連接的利用效率,即HTTP可以復用一條連接。例如某個用戶瀏覽一個網站,他很長一段時間才跳轉到另一個鏈接,似乎保持長連接沒有什么好處,很浪費資源。
但如果這個網站的並發量很大,保持長連接的好處就凸顯了。如果每個請求都需要建立新的TCP連接,那對服務器的開銷是相當的。
建立連接報文首部格式:
Connection:Keep-Alive
Keep-Alive:timeout ; max #參數之間用;分號隔開
參數:max 最大處理事務數量
timeout 連接保持的時間,單位為秒
斷開連接報文首部格式:
Connection:close
Keep-Alive的建立過程
客戶端向服務器在發送請求報文同時在首部添加發送Connection字段》服務器收到請求並處理connection字段》服務器回送Connection:Keep-Alive字段給客戶端》客戶端接收到connection字段》Keep-Alive連接建立成功
服務端自動斷開過程:
客戶端向服務器只是發送內容報文(不包含Connection字段)》服務器收到請求並處理》服務器返回客戶端請求的資源並關閉連接》客戶端接收資源,發現沒有Connection字段,斷開連接
客戶端請求斷開連接過程:
客戶端向服務器發送Connection:close字段》服務器收到請求並處理connection字段》服務器回送響應資源並斷開連接》客戶端接收資源並斷開連接
事務:
在基於TCP連接的http協議中,一個事務表示客戶端向服務端請求一個資源並得到響應返回的過程。這個資源可能是html,css,js文件。
例如:
表示連接的最大處理事務數是5,連接時間是2分鍾
Keep-Alive的限制和規則
(1)事務處理的接收端必須接收到發送端發送的正確的Content-Length字段,或者接收到MIME多部件多媒體類型(允許連接多次發送不同類型的資源)字段,或者接收到Chunked分塊模式字段。以客戶端向服務端發送資源為例(實際上,在一條HTTP信道上,數據傳輸是雙向的,這里只是其中一種情形來講述)。在一個非Keep—Alive的連接中,每一次資源請求都需要創建一個HTTP連接,出錯了重傳不會擔心前后兩次的Content-Length不相同問題。而如果是在Keep-Alive連接中,問題就沒有那么簡單了。想象一下出錯客戶端需要重新發送資源給服務端,而此時服務端端保存的可能還是上一條錯誤報文的Content-Length(可能是0),那么在接收新資源時候就會出現實體內容長度和content-length不一致等問題(這里存在了太多的細節問題)。
(2)不應該與無法確定是否支持Connection首部的代理服務器建立keep-alive連接,以防止出現下面要介紹的啞代理問題。但實際應用中,這一點不是總是能做到。
啞代理問題
盲中繼指的是一些自身沒有處理Connection:Keep-Alive的能力代理服務器。當客戶端向服務端發送Connection字段的時候,通過代理服務器,而代理服務器沒有處理Connection字段,而只將其當一個擴展首部來處理,將報文原原本本發送給服務端,服務端接收到Connecton字段后回送Connection字段。這時代理服務器還是原封不動將報文轉送給客戶端。此時客戶端和服務器都建立了Keep-Alive連接,而代理服務器卻毫不知情。它一直在等待客戶端給它發送斷開連接的字段,因為一個事務已經完成,已經沒有必要保持連接了。這是建立了Keep-Alive連接客戶端繼續互相發送數據,而代理服務器收到的不是Connection:close字段,所以對此就會視而不見,就像‘盲’了一樣。
根據《HTTP權威指南》中的描述,目前瀏覽器解決啞代理問題的一個解決方案使擴展首部字段Proxy-Connection,是由Netscape提出的。大致的實現原理是,客戶端服務器會向代理服務器發送Proxy-Connection字段,如果對方是聰明的代理,它就會用Connection代替Proxy-Connection字段,發送給服務端,服務端接受到Connection字段之后就知道是要建立持久連接,這樣客戶端,代理,服務端三者都會明白需要建立一個持久連接。而如果對方是一個盲中繼,它就會直接把Proxy-Connection當做擴展首部直接轉發給服務端,服務端接收到后發現是Proxy-Connection字段,會自動忽略,也就是說不會建立持久連接,通常接下來事務完成之后就是直接關閉連接了。這樣就不會產生上文所述的問題。
由於本人對這方面知識還接觸比較少,在這里就不對Proxy-Connection作更深入闡述了。
二、HTTP/1.1
客戶端(瀏覽器)如果使用的是HTTP/1.1版本,默認會發送connection:keep alive字段
,如果沒有特殊說明,TCP連接是默認保持的,而需要將一個連接關閉,則需要客戶端發送Connection:close首部字段。
HTTP1.1經過版本的更迭,HTTP1.1逐漸停止了對Keep-alive連接的支持,用一種名為持久連接(persistent connection)的改進型機制設計取代了它。這種機制默認連接建立之后是持久的,也就是說客戶端不需要依靠首部添加Keep-alive字段來保持連接。在希望斷開連接的時候,則需要客戶端首部添加Connection:close字段。
持久連接的限制和規則:
(1)HTTP/1.1對代理服務器的要求更高,它必須能管理與客戶端和服務端之間的持久連接。
(2)由於HTTP/1.1設備隨時可能斷開連接(因為出錯;或者是連接空閑一段時間后,服務端做了邏輯處理,主動關閉;也可能是其他原因),客戶端可能在沒有接收到整條完整的響應,服務端就斷開了連接。所以客戶隨時要做好重新發送請求的准備。
(3)一個用戶客戶端對任何服務器或者代理最多只能維持兩條持久連接,以防止服務器過載。所以作為中間者身份的代理服務器可能需要更多到服務器的連接來支持並發的用戶請求。例如有N個用戶請求不同的服務器,代理需要維護2N條到任意服務器或者父代理的連接。
管道化連接
HTTP/1.1允許在持久連接的基礎上自由選擇使用請求管道。管道的原理很像隊列。在響應到達前,客戶端發送的請求可以放進管道里面,當第一條請求發送到服務端而還客戶端還沒有接收到響應的時候,后面的請求可以接着發送了。為什么可以這樣?這就是管道的作用了,因為管道中的請求是‘提前准備好的’,它無需等待客戶端判斷已經接收到響應了再發送新的請求(這是非管道連接的串行請求)。在高時延的網絡中,管道化連接有利於降低網絡的環回時間,提高網絡傳輸性能。
雖然管道化連接有它強大的優勢,但是管道化連接也有它的局限性。
1)客戶端的請求在管道中是有一定次序的。但由於HTTP報文是沒有順序,響應報文一旦次序出錯,就無法與請求一一對應匹配起來。
2)一般來說,非冪等性(例如POST請求,下文會講述)請求是不希望放進管道的。原因是,管道傳輸中如果出現了錯誤,請求和響應的次序無法得到保障,在一些訂單或者涉及資金的POST提交中,結果是災難性的。
3)在管道傳輸過程中,服務器關閉了連接,這時有許多沒有完成的請求事務,那么客戶端和服務端采取什么機制來確認已經處理和沒有處理的事務這一問題值得商榷。客戶端可能必須重新發送請求連接並且重新發送所有的管道中的請求。
三、重試和冪等性
當事務因為一條持續的連接的意外關閉而被終端,客戶端需要重新發送請求。這種重試在管道化連接中更復雜。
一次HTTP請求的類型可能是GET , POST , HEAD , PUT , DELETE , TRACE , OPTION,如果重試前后對服務端的數據沒有很大影響的請求類型就是冪等性的。顯然,POST是非冪等的。關於冪等性的更多討論,可以看看這篇文章:https://www.jianshu.com/p/178da1e2903c
四、最后,關於連接的關閉:
什么時候控制性地關閉連接,以及如何關閉連接,以達到信道上最高效率的數據傳輸是一個值得研究和討論的問題。《HTTP權威指南》中也沒有提供一些可行的方案。以后有接觸到相關知識,我會繼續補充。
參考文章:https://www.jianshu.com/p/178da1e2903c