nginx對keepalive和pipeline請求處理分析

來源:互聯網
上載者:User

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

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

這次主要來看nginx中對keepalive和pipeline的處理,這裡概念就不用介紹了。直接來看nginx是如何來做的。

首先來看keepalive的處理。我們知道http 1.1中keepalive是預設的,除非用戶端顯式的指定connect頭為close。下面就是nginx判斷是否需要keepalive的代碼。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

void

ngx_http_handler(ngx_http_request_t *r)

{

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

switch(r->headers_in.connection_type) {

case0:

//如果版本大於1.0則預設是keepalive

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

break;

caseNGX_HTTP_CONNECTION_CLOSE:

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

r->keepalive = 0;

break;

caseNGX_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

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

staticvoid

ngx_http_finalize_connection(ngx_http_request_t *r)

{

ngx_http_core_loc_conf_t *clcf;

clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

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

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

if(!ngx_terminate

&& !ngx_exiting

&& r->keepalive

&& clcf->keepalive_timeout > 0)

{

ngx_http_set_keepalive(r);

return;

}elseif(r->lingering_close && clcf->lingering_timeout > 0) {

ngx_http_set_lingering_close(r);

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有重用的話,減少一次分配。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

hc = r->http_connection;

b = r->header_in;

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

if(b->pos < b->last) {

/* the pipelined request */

if(b != c->buffer) {

/*

* If the large header buffers were allocated while the previous

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

* the pipelined request (see ngx_http_init_request()).

*

* Now we would move the large header buffers to the free list.

*/

cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

//如果free為空白,則建立

if(hc->free== NULL) {

//可以看到是large_client_headers的個數

hc->free= ngx_palloc(c->pool,

cscf->large_client_header_buffers.num *sizeof(ngx_buf_t *));

if(hc->free== NULL) {

ngx_http_close_request(r, 0);

return;

}

}

//然後清理當前的request的busy

for(i = 0; i < hc->nbusy - 1; i++) {

f = hc->busy[i];

hc->free[hc->nfree++] = f;

f->pos = f->start;

f->last = f->start;

}

//儲存當前的header_in buf,以便與下次給free使用。

hc->busy[0] = b;

hc->nbusy = 1;

}

}

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

r->keepalive = 0;

ngx_http_free_request(r, 0);

c->data = hc;

//設定定時器

ngx_add_timer(rev, clcf->keepalive_timeout);

//然後設定可讀事件

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

ngx_http_close_connection(c);

return;

}

wev = c->write;

wev->handler = ngx_http_empty_handler;

然後接下來這部分就是對pipeline的處理。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

if(b->pos < b->last) {

ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request");

#if (NGX_STAT_STUB)

(void) ngx_atomic_fetch_add(ngx_stat_reading, 1);

#endif

//設定標記。

hc->pipeline = 1;

c->log->action ="reading client pipelined request line";

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

rev->handler = ngx_http_init_request;

ngx_post_event(rev, &ngx_posted_events);

return;

}

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

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

hc->request = NULL;

}

b = c->buffer;

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

/*

* the special note for ngx_http_keepalive_handler() that

* c->buffer's memory was freed

*/

b->pos = NULL;

}else{

b->pos = b->start;

b->last = b->start;

}

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

if(hc->busy) {

for(i = 0; i < hc->nbusy; i++) {

ngx_pfree(c->pool, hc->busy[i]->start);

hc->busy[i] = NULL;

}

hc->nbusy = 0;

}

設定keepalive的handler。

1

2

3

4

5

6

7

8

9

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

rev->handler = ngx_http_keepalive_handler;

if(wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {

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

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

b = c->buffer;

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

* to keep the buffer size.

*/

//重新分配buf

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

if(b->pos == NULL) {

ngx_http_close_connection(c);

return;

}

b->start = b->pos;

b->last = b->pos;

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

}

然後嘗試讀取資料,如果沒有可讀資料,則會將控制代碼再次加入可讀事件

1

2

3

4

5

6

7

8

9

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

c->log_error = NGX_ERROR_INFO;

if(n == NGX_AGAIN) {

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

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

r = hc->request;

if(r) {

//如果存在,則我們重用前一個request.

ngx_memzero(r,sizeof(ngx_http_request_t));

r->pipeline = hc->pipeline;

//如果nbusy存在

if(hc->nbusy) {

//則儲存這個header_in,然後下面直接解析。

r->header_in = hc->busy[0];

}

}else{

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

if(r == NULL) {

ngx_http_close_connection(c);

return;

}

hc->request = 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教程有興趣的朋友有所協助。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.