Keep-Alive下Http如何區分多個請求

使客戶端到服務器端的鏈接持續有效,當出現對服務器的後繼請求時,Keep-Alive功能避免了創建或者從新創建鏈接。
HTTP/1.0中默認使用Connection:close, 在HTTP/1.1中已經默認使用Connection: keep-alive。TCP協議層默認並不開啓KeepAlive功能。
java

用法

http1.1中因默認支持長鏈接,因此若是不但願使用則須要在header中指明connection的值爲close;而server也不想支持,則在response中也須要明確說明connection的值爲close。
HTTP Connection的 close設置容許客戶端或服務器中任何一方關閉底層的鏈接時雙方都會要求在處理請求後關閉它們的TCP鏈接。即不管request仍是response的header中包含了值爲close的connection,都代表當前正在使用的tcp連接在請求處理完畢後會被斷掉,之後client再進行新的請求時就必須建立新的tcp連接了。(程序中能夠在過濾器中加入:response.setHeader("connection", "close");)
瀏覽器在請求頭部添加 Connection:keep-alive,以此告知服務器本身支持長鏈接方式,而假若服務器也支持,那麼就在響應頭部添加 Connection:keep-alive,從而告訴瀏覽器長鏈接。服務器還能夠經過 Keep-Alive:timeout=10, max=100 的頭部告訴瀏覽器「10 秒算超時時間,最長不能超過 100 秒」。 客戶端和服務端均可以設置timeout和max屬性值,但http鏈接保持時間是由服務端的消息頭connection字段和keep-alive字段定的linux

//org.apache.http.impl.execchain.MainClientExec#execute
......
 
//從鏈接池中lease connection
final HttpClientConnectionmanagedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS);
 
......
 
//將conenction封裝在ConnectionHolder中
final ConnectionHolder connHolder = new ConnectionHolder(this.log, this.connManager, managedConn);
 
......
 
// The connection is in or can be brought to a re-usable state.
//若是返回值消息頭中connection設置爲close,則返回false
if (reuseStrategy.keepAlive(response, context)) {
    // Set the idle duration of this connection
    //取出response消息頭中,keep-alive的timeout值
    final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
    if (this.log.isDebugEnabled()) {
        final String s;
        if (duration > 0) {
            s = "for " + duration + " " + TimeUnit.MILLISECONDS;
        } else {
            s = "indefinitely";
        }
        this.log.debug("Connection can be kept alive " + s);
    }
    //設置失效時間
    connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);
    connHolder.markReusable();
} else {
    connHolder.markNonReusable();
}

TCP KeepAlive與 Http keep-alive 區別

http keep-alive 意圖在於鏈接複用。是爲了讓TCP存活更久以便複用TCP鏈接,在一個TCP鏈接上進行屢次的HTTP請求從而提升性能。
tcp
KeepAlive是TCP的一種檢測TCP鏈接情況的保鮮機制, 意圖在於保活、心跳,檢測鏈接錯誤。
TCP通常而言爲服務器端提供保活功能: 若是客戶端已經消失,使得服務器上保留一個半開放的鏈接,而服務器又在等待來自客戶端的數據,則服務器將永遠等待客戶端的數據, 保活功能就是試圖在服務器端檢測到這種半開放的鏈接。nginx

保活心跳機制

及時有效地檢測到一方的非正常斷開,保證鏈接的資源被有效利用。apache

  1. 自實現
    大體方法是:服務器在一個 Timer事件中定時向客戶端發送一個短小精悍的數據包,而後啓動一個低級別的線程,在該線程中不斷檢測客戶端的迴應, 若是在必定時間內沒有收到客戶端的迴應,即認爲客戶端已經掉線;一樣,若是客戶端在必定時間內沒有收到服務器的心跳包,則認爲鏈接不可用。
  2. Keep-Alive
    keepalive只能檢測鏈接是否存活,不能檢測鏈接是否可用。eg. 服務器由於負載太高致使沒法響應請求可是鏈接仍然存在,此時keepalive沒法判斷鏈接是否可用。
    • HTTP保活:
      Httpd守護進程,通常都提供了keep-alive timeout時間設置參數。eg:  nginxkeepalive_timeout,和ApacheKeepAliveTimeout
      一個http產生的tcp鏈接在傳送完最後一個響應後,還須要hold住 keepalive_timeout秒後,若是守護進程在這個等待的時間裏,一直沒有收到瀏覽器發過來http請求,則關閉這個http鏈接。
      Tomcat中的相關設置,在conf/server.xml 中的Connector 元素中:
      keepAliveTimeout:
      The number of milliseconds this Connector will wait for another HTTP request before closing the connection. The default value is to use the value that has been set for the connectionTimeout attribute.
      
      maxKeepAliveRequests:
      The maximum number of HTTP requests which can be pipelined until the connection is closed by the server. Setting this attribute to 1 will disable HTTP/1.0 keep-alive, as well as HTTP/1.1 keep-alive and pipelining. Setting this to -1 will allow an unlimited amount of pipelined or keep-alive HTTP requests. If not specified, this attribute is set to 100.
    • TCP保活:   
      沒有一個全局的選項去開啓TCP的KeepAlive,可在TCP的socket中經過setsockopt系統調用針對單獨的socket進行設置開啓(使用時須要#include <netinet/tcp.h>, 不然SOL_TCP和TCP_KEEPIDLE等3個宏找不到)。
      int keepAlive = 1; 	 // 開啓keepalive屬性. 缺省值: 0(關閉)
      int keepIdle = 60;	 // 若是在60秒內沒有任何數據交互,則進行探測. 缺省值:7200(s)
      int keepInterval = 5;	// 探測時發探測包的時間間隔爲5秒. 缺省值:75(s)
      int keepCount = 2;	 // 探測重試的次數. 所有超時則認定鏈接失效..缺省值:9(次)
      setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));
      setsockopt(s, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
      setsockopt(s, SOL_TCP, TCP_KEEPINTVL, (void*)&keepInterval, sizeof(keepInterval));
      setsockopt(s, SOL_TCP, TCP_KEEPCNT, (void*)&keepCount, sizeof(keepCount));
      // TCP_KEEPCNT : 覆蓋 tcp_keepalive_probes ;
      // TCP_KEEPIDLE : 覆蓋tcp_keepalive_time ;
      // TCP_KEEPINTVL : 覆蓋 tcp_keepalive_intvl int keepAlive = 1;
      • 在windows系統中能夠經過修改註冊表等來達到調整:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
        • KeepAliveTime: 默認7,200,000毫秒(兩個小時)。若是這段時間內沒有活動,則會發送保持活動信號。默認狀況下不發送保活數據包。
          若是須要對丟失接收方敏感,更快地發現丟失了接收方,須要考慮減少這個值。若是長期不活動的空閒鏈接出現次數較多,而丟失接收方的狀況出現較少,可能會要提升該值以減小開銷。缺省狀況下,若是空閒鏈接 7200000 毫秒(2 小時)內沒有活動,Windows 就發送保持活動的消息。一般,1800000 毫秒是首選值,從而一半的已關閉鏈接會在 30 分鐘內被檢測到。 
        • KeepAliveInterval:默認1000毫秒(1 秒)。 肯定在收到響應以前,保活重傳之間的時間間隔。
          一旦收到一個響應,將由 KeepAliveTime 值從新控制在下一次保活傳輸以前的延遲。若是通過 TcpMaxDataRetransmissions 指定的從新傳輸次數後仍無響應,將放棄鏈接。 
          缺省狀況下,在未收到響應而從新發送保持活動的消息以前,Windows 會等待 1000 毫秒(1 秒)。若是指望較長的響應時間,須要提升該值以減小開銷。若是須要減小花在驗證接收方是否已丟失上的時間,請考慮減少該值或 TcpMaxDataRetransmissions 值。
      •  在linux中:三個內核參數:tcp_keepalive_time(開啓keepalive的閒置時長)tcp_keepalive_intvl(keepalive探測包的發送間隔)和 tcp_keepalive_probes (若是不予應答,探測包的發送次數)。
        修改配置文件, 對整個系統全部的socket有效。(在系統重啓後三個參數的值又會恢復到默認值)
        #cat /proc/sys/net/ipv4/tcp_keepalive_time  7200  
        
        #cat /proc/sys/net/ipv4/tcp_keepalive_intvl  75  
        
        #cat /proc/sys/net/ipv4/tcp_keepalive_probes  9
        
        
        
        #echo 60 > /proc/sys/net/ipv4/tcp_keepalive_time  
        
        #echo 5 > /proc/sys/net/ipv4/tcp_keepalive_intvl  
        
        #echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes
        全局設置可更改/etc/sysctl.conf, 使用 sudo sysctl -p當即生效;(永久有效)
        net.ipv4.tcp_keepalive_intvl = 20
        net.ipv4.tcp_keepalive_probes = 3
        net.ipv4.tcp_keepalive_time = 60

HTTP如何區分多個請求

Content-Length

表示實體內容的長度。瀏覽器經過這個字段來判斷當前請求的數據是否已經所有接收。c#

因此,當瀏覽器請求的是一個靜態資源時,即服務器能明確知道返回內容的長度時,能夠設置Content-Length來控制請求的結束。但當服務器並不知道請求結果的長度時,如一個動態的頁面或者數據,Content-Length就沒法解決上面的問題,這個時候就須要用到Transfer-Encoding字段。windows

Transfer-Encoding

表示傳輸編碼。
還有一個相似的字段叫作:Content-Encoding。區別是Content-Encoding用於對實體內容的壓縮編碼Content-Encoding: gzip; Transfer-Encoding則改變了報文的格式。
當服務端沒法知道實體內容的長度時,可指定Transfer-Encoding:chunked (還可同時指定Transfer-Encoding: gzip),代表實體內容數據不只是gzip壓縮的,仍是分塊傳遞的。最終當瀏覽器接收到一個長度爲0的chunked時, 標識當前請求內容已所有接收。瀏覽器

chunked

分塊編碼, 標識將數據分紅一塊一塊的發出。Chunked編碼將使用若干個Chunk串連而成,由一個標明長度爲0 的chunk標示結束
chunk-size指定十六進制的數字表明後面chunk-data的字節長度,若是是「0」,則表示chunk-size爲0,該chunk爲last-chunk,無chunk-data部分。服務器

Chunked-Body   = *chunk            //0至多個chunk

last-chunk         //最後一個chunk

trailer            //尾部

CRLF               //結束標記符

chunk          = chunk-size [ chunk-extension ] CRLF   chunk-data CRLF // 單個chunk內容

chunk-size     = 1*HEX

last-chunk     = 1*("0") [ chunk-extension ] CRLF

chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )

chunk-ext-name = token

chunk-ext-val  = token | quoted-string

chunk-data     = chunk-size(OCTET)

trailer        = *(entity-header CRLF)

總結

一、Content-Length若是存在而且有效的話,則必須和消息內容的傳輸長度徹底一致。(若是太短則會截斷,過長則會致使超時)socket

二、若是存在Transfer-Encoding(重點是chunked),則在header中的Content-Length會被忽視。tcp

三、若是採用短鏈接,則直接能夠經過服務器關閉鏈接來肯定消息的傳輸長度。

引貼: Windows系統下的TCP參數優化
          HTTP長鏈接、短鏈接使用及測試

相關文章
相關標籤/搜索