http之keepalive

短連接&長連接&並行連接

再說keep-alive之前,先說說HTTP的短連接&長連接。

  • 短連接

    所謂短連接,就是每次請求一個資源就建立連接,請求完成後連接立馬關閉。每次請求都經過“創建tcp連接->請求資源->響應資源->釋放連接”這樣的過程

  • 長連接

    所謂長連接(persistent connection),就是隻建立一次連接,多次資源請求都複用該連接,完成後關閉。要請求一個頁面上的十張圖,只需要建立一次tcp連接,然後依次請求十張圖,等待資源響應,釋放連接。

  • 並行連接

    所謂並行連接(multiple connections),其實就是併發的短連接。

keep-alive

具體client和server要從短連接到長連接最簡單演變需要做如下改進:

  1. client發出的HTTP請求頭需要增加Connection:keep-alive字段
  2. Web-Server端要能識別Connection:keep-alive字段,並且在http的response裏指定Connection:keep-alive字段,告訴client,我能提供keep-alive服務,並且"應允"client我暫時不會關閉socket連接

在HTTP/1.0裏,爲了實現client到web-server能支持長連接,必須在HTTP請求頭裏顯示指定

Connection:keep-alive

在HTTP/1.1裏,就默認是開啓了keep-alive,要關閉keep-alive需要在HTTP請求頭裏顯示指定

Connection:close

現在大多數瀏覽器都默認是使用HTTP/1.1,所以keep-alive都是默認打開的。一旦client和server達成協議,那麼長連接就建立好了。

接下來client就給server發送http請求,繼續上面的例子:請求十張圖片。如果每次"請求->響應"都是獨立的,那還好,10張圖片的內容都是獨立的。但是如果pipeline模式,上一個請求還沒響應,下一個請求就發出,這樣併發地發出10個請求,對於10個response client要怎麼區分呢?而HTTP協議又是沒有辦法區分的,所以這種情況下必須要求server端地響應是順序的,通過Conten-Length區分每次請求,這還只是針對靜態資源,那對於動態資源無法預知頁面大小的情況呢?我還沒有深入研究,可以查看https://www.byvoid.com/blog/http-keep-alive-header

另外注意: 指定keep-alive是一種client和server端儘可能需要滿足的約定,client和server可以在任意時刻都關閉keep-alive,彼此都不應該受影響。

Nginx keepa-alive配置

具體到Nginx的HTTP層的keepalive配置有

  • keepalive_timeout
    Syntax: keepalive_timeout timeout [header_timeout];
    Default:    keepalive_timeout 75s;
    Context:    http, server, location

The first parameter sets a timeout during which a keep-alive client connection will stay open on the server side. The zero value disables keep-alive client connections. The optional second parameter sets a value in the “Keep-Alive: timeout=time” response header field. Two parameters may differ.

  • keepalive_requests
    Syntax: keepalive_requests number;
    Default:    keepalive_requests 100;
    Context:    http, server, location

Sets the maximum number of requests that can be served through one keep-alive connection. After the maximum number of requests are made, the connection is closed.

可以看看Nginx的關於 keepalive_timeout 是實現


./src/http/ngx_http_request.c

static void
ngx_http_finalize_connection(ngx_http_request_t *r){
...
    if (!ngx_terminate
         && !ngx_exiting
         && r->keepalive
         && clcf->keepalive_timeout > 0)
    {
        ngx_http_set_keepalive(r);
        return;
    }
...
}


static void
ngx_http_set_keepalive(ngx_http_request_t *r){

    //如果發現是pipeline請求,判斷條件是緩存區裏有N和N+1個請求同時存在
    if (b->pos < b->last) {

        /* the pipelined request */
    }
    // 本次請求已經結束,開始釋放request對象資源
    r->keepalive = 0;

    ngx_http_free_request(r, 0);

    c->data = hc;

    // 如果嘗試讀取keep-alive的socket返回值不對,可能是客戶端close了。那麼就關閉socket
    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
        ngx_http_close_connection(c);
        return;
    }

    //開始正式處理pipeline
    ...

    rev->handler = ngx_http_keepalive_handler;
    ...
    // 設置了一個定時器,觸發時間是keepalive_timeout的設置
    ngx_add_timer(rev, clcf->keepalive_timeout);

    ...

}


static void
ngx_http_keepalive_handler(ngx_event_t *rev){

    // 發現超時則關閉socket
    if (rev->timedout || c->close) {
        ngx_http_close_connection(c);
        return;
    }

    // 讀取keep-alive設置從socket
    n = c->recv(c, b->last, size);
    if (n == NGX_AGAIN) {
        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
            ngx_http_close_connection(c);
            return;
        }
        ...
    }

    //此處尚有疑惑?
    ngx_reusable_connection(c, 0);

    c->data = ngx_http_create_request(c);
    // 刪除定時器
    ngx_del_timer(rev);
    // 重新開始處理請求
    rev->handler = ngx_http_process_request_line;
    ngx_http_process_request_line(rev);
}

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章