>  기사  >  백엔드 개발  >  Keepalive 및 파이프라인 요청 처리에 대한 nginx 분석

Keepalive 및 파이프라인 요청 처리에 대한 nginx 분석

WBOY
WBOY원래의
2016-08-08 09:30:271151검색

원본 기사, 재인쇄 시 표시해 주세요: Reprinted from pagefault

이 기사의 링크 주소: keepalive 및 파이프라인 요청 처리에 대한 nginx 분석

이번에는 주로 nginx를 살펴보세요. Keepalive 및 파이프라인 처리와 관련하여 여기에 개념을 소개할 필요가 없습니다. nginx가 어떻게 작동하는지 직접 살펴보겠습니다. 먼저 Keepalive 처리 과정을 살펴보겠습니다. 클라이언트가 명시적으로 연결 헤더를 close로 지정하지 않는 한, keepalive가 http 1.1의 기본값이라는 것을 알고 있습니다. 다음은 keepalive가 필요한지 여부를 결정하는 nginx의 코드입니다.

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;

        }

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

}

12 3456789101112131415161718192021
void ngx_http_handler(ngx_http_request_t *r){.... ................................. 스위치(r->headers_in.connection_type) { case 0://버전이 1.0보다 큰 경우 기본값은 연결 유지입니다 r - >keepalive = (r->http_version > NGX_HTTP_VERSION_10);      break; caseNGX_HTTP_CONNECTION_CLOSE://연결 헤더가 close로 지정된 경우 no keepalive가 필요합니다. r->keepalive = 0; break 코드>; 케이스NGX_HTTP_CONNECTION_KEEP_ALIVE: /code>r->keepalive = 1; break;; 코드> }.......... ..... ..}

그러면 keepalive는 현재 http 요청이 실행된 후 바로 현재 연결이 닫히지 않는다는 것을 의미하므로 nginx의 keepalive 관련 처리도 요청 기능을 정리합니다. nginx가 요청을 정리하는 함수는 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);

}

12 34567891011121314151617181920212223
staticvoidngx_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가 설정되고 시간 제한이 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); 반환; } ngx_http_close_request(r, 0);}
위에서 ngx_http_set_keepalive를 통해 keepalive가 설정되어 있음을 알 수 있습니다. 다음으로 이 기능에 대해 자세히 살펴보겠습니다. 이 함수에서는 파이프라인 요청이 부수적으로 처리되므로 함께 살펴보겠습니다. 먼저 nginx가 클라이언트에서 읽은 데이터에 더 많은 데이터가 포함되어 있다고 가정합니다. 즉, 현재 요청을 구문 분석한 후에도 일부 데이터가 아직 남아 있어 파이프라인 요청으로 간주됩니다. 또 다른 매우 중요한 점은 http_connection입니다. 이전 블로그에서 큰 헤더를 할당해야 할 경우 hc->free에서 먼저 가져온 다음 생성된다는 것을 알고 있습니다. hc->busy를 관리하도록 하세요. 그리고 이 buf는 여기서 재사용됩니다. 왜냐하면 큰 buf가 크면 두 번째로 다시 할당해야 하기 때문입니다. 여기서 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;

        }

    }

다음 부분은 무료 요청 및 연결 유지 타이머 설정입니다.

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;

12 3

4

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;

    }

56789 10 1112131415
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); 반환 ;wev = c->write;wev->handler = ngx_http_empty_handler; 그런 다음 다음 부분은 파이프라인 처리입니다.
12 3456789101112131415 if(b->pos < b->last) {ngx_log_debug0( NGX_LOG_DEBUG_HTTP, c->로그, 0, "파이프라인 요청");#if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);#endif//태그를 설정합니다. hc->파이프라인 = 1;c->로그->액션 = "클라이언트 파이프라인 요청 라인 읽기";//그런 다음 게시 대기열에 넣고 처리를 계속합니다. code >rev->handler = ngx_http_init_request;ngx_post_event(rev, &ngx_posted_events);반환; 이 맨 아래에 도달하면 파이프라인 요청이 아니라는 뜻이므로 요청과 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;

    }

12 3

4

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;

        }

    }

567891011121314151617181920212223 24252627282930
if(ngx_pfree(c->pool, r) == NGX_OK) { hc->request = NULL; } b = c->buffer; if(ngx_pfree (c->풀, b->시작) == NGX_OK) {   /* * ngx_http_keepalive_handler()에 대한 특별 참고 사항 * c->버퍼의 메모리가 해제되었습니다 코드> 코드> */ b->pos = NULL; 코드> } else{    b->pos = b->start; code>else code><code> b->last = b->start; }.......................... ......... ................................ if(hc->busy) {    for code>(i = 0; i < hc->nbusy; i++) {    ngx_pfree(c->pool, hc->busy[i] ->start); hc->busy[i] = NULL; } hc->nbusy = 0; <code>}
Keepalive 처리기를 설정합니다.
12 3456789 //이 함수는 나중에 자세히 분석할 예정입니다. 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) { code><code>ngx_http_close_connection(c);    return; } }
마지막 단계는 tcp push 처리입니다. 지금은 여기서 소개하지 않겠습니다. 다음으로 nginx의 tcp push 동작을 소개하는 특별한 블로그를 준비하겠습니다. 그런 다음 ngx_http_keepalive_handler 함수를 살펴보겠습니다. 이 함수는 연결에 다시 읽을 수 있는 이벤트가 있을 때 호출됩니다. 이 핸들러는 비교적 간단합니다. 새 buf를 생성한 다음 http 요청 실행을 다시 시작합니다(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;

    }

12 3

4

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;

}

567

1

ngx_http_init_request(rev);

89101112131415161718192021
 b = c->버퍼; 크기 = b->end - b->start; 코드> 코드> if(b->pos == NULL) { /* * c->버퍼의 메모리는 ngx_http_set_keepalive()에 의해 해제되었습니다. * 단, c->buffer->start와 c->buffer->end는 변경되지 않았습니다.                                                                                                                                                             code>//buf 재할당 b->pos = ngx_palloc(c->pool, size) ; code><code> if(b->pos == NULL) { ngx_http_close_connection(c);                                                                                              } b->start = b->pos;                                                                                                                                                >위치 + 크기 }
table>드디어 데이터를 읽어오면 요청 처리에 들어갑니다.
1234 56789 n = c->recv(c, b->마지막, 크기); code>c->log_error = NGX_ERROR_INFO;if(n == NGX_AGAIN) { if(ngx_handle_read_event(rev, 0 ) != NGX_OK) { ngx_http_close_connection(c); } 반환;}
1 ngx_http_init_request(rev);
마지막으로 ngx_http_init_request 함수를 살펴보겠습니다. 이번에는 파이프라인이 요청될 때 nginx가 요청을 재사용하는 방법을 주로 살펴보겠습니다.
여기에서 hc->busy[0]에 주의하세요. 파이프라인 요청인 경우 이전에 구문 분석되지 않은 요청 header_in을 저장한다는 것을 이미 알고 있습니다. 이는 두 번째 헤더를 읽었을 수 있기 때문입니다. 파이프라인 요청의 일부 헤더입니다.

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;

12 3

4

567891011121314151617181920212223 2425
//요청 가져오기, 여기서 우리는 파이프라인 요청에서 이전 요청을 저장할 것이라는 것을 알고 있습니다.<code> r = hc->request; if code >(r) {//존재하는 경우 이전 요청을 재사용합니다. ngx_memzero(r, sizeof(ngx_http_request_t)); r->pipeline = hc-> 파이프라인;//nbusy가 존재하는 경우 if(hc ->nbusy) {//이 header_in을 저장한 후 바로 아래에서 구문 분석합니다. r->header_in = hc->busy[0]; 코드> 코드>} } else{ r = ngx_pcalloc(c->pool, 크기(ngx_http_request_t)); if(r == NULL) { ngx_http_close_connection(c); 반환; } hc->request = r; }//요청 저장 c->data = r;
위 코드와 이전 블로그를 통해 큰 헤더가 주로 파이프라인용이라는 것을 알 수 있습니다. 왜냐하면 파이프라인에서는 이전 요청이 다음 요청보다 더 많이 읽는 경우 헤더가 몇 개 있는 경우이기 때문입니다. , 다음 번에 구문 분석할 때 원래 할당된 client_header_buffer_size를 초과할 수 있습니다. 이때 큰 헤더인 헤더를 재할당해야 하므로 여기서 httpconnection은 주로 파이프라인 상황을 위한 것이며 Keepalive 연결이 다음과 같은 경우입니다. 파이프라인 요청이 아닙니다. 메모리를 절약하기 위해 이전 요청이 해제됩니다. 위 내용은 관련 측면을 포함하여 keepalive 및 파이프라인 요청 처리에 대한 nginx의 분석을 소개합니다. PHP 튜토리얼에 관심이 있는 친구들에게 도움이 되기를 바랍니다.
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:기본 PHP 학습(3부)다음 기사:기본 PHP 학습(3부)