首頁  >  文章  >  後端開發  >  nginx對keepalive和pipeline請求處理分析

nginx對keepalive和pipeline請求處理分析

WBOY
WBOY原創
2016-08-08 09:30:271151瀏覽

原創文章,轉載請註明: 轉載自pagefault

本文連結地址: nginx對keepalive和pipeline請求處理分析

這次主要來看nginx中對keepalive和pipeline的處理,這裡概念就不用介紹了。直接來看nginx是如何來做的。 首先來看keepalive的處理。我們知道http 1.1中keepalive是預設的,除非客戶端明確的指定connect頭為close。下面就是nginx判斷是否需要keepalive的程式碼。

1

2

3

4

5

6

4

58

11

12

13

14

15

16

17

18

19

20

21

r)

{

..... ....................................

        switch

(r->headers_in .connection_type) {

        case

0:      r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);

<p><code>            break;

  

//如果指定connection頭為close則不需要keepalive

            

            break

;

        case

NGX_HTTP_CONNECTION_KEEP_ALIVE:

            

r->keepalive = 1;

            

break;

        

} ..................................

}


然後我們知道keepalive也就是當前的http request執行完畢後並不會直接關閉當前的連接,因此nginx的keepalive的相關處理也就是清理request的函數中。 nginx清理requst的函數是ngx_http_finalize_request,這個函數中會呼叫ngx_http_finalize_connection來釋放連接,而keepalive的相關判斷就在這個函數中。

{

1

2

3

4

5

6

4

58

11

12

13

14

15

16

17

18

19

20

21

222

ngx_http_finalize_connection(ngx_http_request_t *r)

    ngx_http_core_loc_conf_t  *clcf;

    

..................... ................................................

//可以看到如果設定了keepalive,且timeout大於0,就進入keepalive的處理。

    

if

(!ngx_terminate

           

&& r->keepalive

         

&&Dhcf-keep

{        

ngx_http_set_keepalive(r);

    

} elseif

(r->lingering_close && clcf->lingering_timeout > 0) {_  

        return;

    }

    ngx_http_close_request(r, 0);

}}

}

]

透過上面我們能看到keepalive是透過ngx_http_set_keepalive來設定的,接下來我們就來詳細的看這個函數。 在這個函數裡面會順帶處理pipeline的請求,因此我們一併來看,首先nginx是如何區分pipeline請求的呢,它會假設如果從客戶端讀取的數據多包含了一些數據,也就是解析完目前的request之後,還有一部分數據,這時,就認為是pipeline請求。 還有一個很重要的地方就是http_connection,我們在前面的blog知道,如果需要alloc large header時候,會先從hc->free裡面取,如果沒有的話,會新建,然後交給hc->busy去管理。而這個buf,就會在這裡被重用,因為large buf的話,需要重新alloc第二次,如果這裡buf有重用的話,減少一次分配。

1415

1

2

3

4

5

6

4

58

11

12

13

14

15

16

17

18

19

20

21

22

22233 7

28

29

30

31

32

33

34

35

36

37

38

    

hc = r->http_connection;

    

b = r->header_in;

//一般情況下,當解析完header_in之後,pos會設定為last。也就是讀取到的資料剛好是一個完整的http請求.當pos小於last,則表示可能是pipeline請求。

    

if

(b->pos last) {

          if(b != c-> buffer) {

            /*

我the previous

             * request processing then we do not use c->buffer for* request processing then we do not use c->buffer for

             

* the pipelined request (see ngx_http_init_request()).

             *

<p><code>             * Now we would move the large header buffers to the free list.

<p><code>             */

<p><code>            cscf = ngx_http_get_module_srv_conf(r,          

if

(hc->free

== NULL) {

//可以看到是large_client_headers的數量

<p><code>             

                  

cscf->large_client_header_buffers.num * sizeof

(ngx_buf_t *));

             {

<p><code>                    

ngx_http_close_request(r, 0);

ngx_http_close_request(r, 0);                     return;

   

<p><code>            }

//然後清除目前的request 

(i = 0; i nbusy - 1; i++) {

              f = hc->busy[i];

                 

hc->mm             f->pos = f->start;

<p><code>                f->last = f->start; //保存目前的header_in buf,以便與下次給free使用。

            hc->busy[0] = b;

        }

    }

    }

然後接下來這部分就是free request,並設定keepalive 定時器.

1

8 9

10

1112

13

    

ngx_http_free_request(r, 0);     _fetch_add(ngx_stat_reading, 1);

    

c->data = hc;

//設定定時器

    //然後設定可讀事件

if

(ngx_handle_read_event(rev, 0) != NGX_OK) {

            

return

;

}

 wev = c->write;

    

wev->handler = ngx_http_empty_handler;

的部分。

123

45

6458 11

12

1314

15

    

if

(b->pos last) {_N TP, c->

log, 0, "pipelined request"

);#if (NGX_STAT_STUB)

#endif

//設定標記。

;
        

hc->pipeline = 1;

    

"reading client pipelined request line"

;

//然後丟進post queue,繼續處理.

.

        

ngx_post_event(rev , &ngx_posted_events);

        

return

到達下面,則表示不是pipeline的請求,因此就開始對request, http_connection 進行清理工作。

}

1

2

3

4

5

6

4

58

11

12

13

14

15

16

17

18

19

20

21

22

22233 7

28

29

30

    

if

(ngx_pfree(c->pool, r) == NGX_OK) {

      

    b = c->buffer;

    

if(ngx_pfree(c->pool, b->start) == NGX_ 

         

* the special note for ngx_http_keepalive_handler() that

         

* c->buffer 

        

b->pos = NULL;    

}

else{

        b->pos = b->start;start>

    }

. .................................................. ..................

    if(hc->busy) {

   hc->nbusy; i++) {            ngx_pfree(c->pool,  

hc->busy[i] = NULL;         

}        

hc->

設定keepalive的handler。

1

234

5

//後面會詳細分析這個函數

    

rev->handler = ngx_http_keepalive_handler;

    

if(weal_wid) (Wavw)_o_af;

        if(ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {

            ngx_http_close_connection(c); return

;
        }

   

最後是對tcp push的處理,這裡暫時我就不介紹了,接下來我會有專門一篇blog來介紹nginx對tcp push的操作。 然後我們來看ngx_http_keepalive_handler函數,這個函數是處理keepalive連接,當在連接上再次有可讀的事件的時候,就會調用這個handler。 這個handler比較簡單,就是建立新的buf,然後重新開始一個http request的執行(呼叫ngx_http_init_request)。

        

1

2

3

4

5

6

4

58

11

12

13

14

15

16

17

18

19

20

21

    

size = b->end - b-> start;

    

if(b->pos == NULL) {

      * The c->buffer's memory was freed by ngx_http_set_keepalive().

         * However, the c->buffer->start and c->buffer->end were not changed

         

*/

//重新分配buf

        

b->pos = ngx_palloc(c->pool, size);

(b->pos == NULL) {             

ngx_http_close_connection(c);            

return;

        }

        b->start = b->pos;

        b-> last = b->pos;

        b->end = b->pos + size;

  然後嘗試讀取數據,如果沒有可讀數據,則會再次加入可讀事件

1

2

34

n = c->recv(c, b->last, size);

c->log_error = NGX_ERROR_INFO;

if

  if(ngx_handle_read_event(rev, 0) != NGX_OK) {

ngx_http_close_connection(c); }    

return;

}

最後如果讀取了數據,則進入request的處理。

1

ngx_http_init_request(rev);

ngx_http_init_request(rev);

最後我們再來看ngx_http_init_request函數,這次主要來看當時pipeline請求的時候,nginx是如何來重複使用request的。
這裡要注意hc->busy[0],前面我們知道,如果是pipeline請求,我們會保存前面沒有解析完畢的request header_in,這是因為我們可能已經讀取了pipeline請求的第二個請求的一些頭。

    

1

2

3

4

5

6

4

58

11

12

13

14

15

16

17

18

19

20

21

22

//取得request,這裡我們知道,在pipeline請求中,我們會保存前一個request.

    

r = hc->request;

request.

        

ngx_memzero(r, sizeof(ngx_http_request_t));

        r->p         

if

(hc->nbusy) {

//則儲存這個header_in,然後下面直接解析。             r->header_in = hc->busy[0];     

} else{

        

r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t));

            

ngx_http_close_connection(c);            

        }

        hc- = r; //保存請求    

c->data = r;

從上面的程式碼,然後再結合我前一篇blog,我們就知道large header主要是針對pipeline的了,因為在pipeline中,前一個request如果多讀了下一個request的一些頭的話,這樣子下次解析的時候就有可能會超過原本分配的client_header_buffer_size,此時,我們就需要重新分配一個header,也就是large header了,所以這裡httpconnection主要就是針對pipeline的情況,而keepalive的連接並不是pipeline的請求的話,為了節省內存,就把前一個request釋放掉了. 以上就介紹了nginx對keepalive和pipeline請求處理分析,包括了方面的內容,希望對PHP教程有興趣的朋友有所幫助。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn