ホームページ  >  記事  >  バックエンド開発  >  キープアライブとパイプラインリクエスト処理のnginx分析

キープアライブとパイプラインリクエスト処理のnginx分析

WBOY
WBOYオリジナル
2016-08-08 09:30:271198ブラウズ

元の記事、転載の際は明記してください:pagefaultから転載

この記事のリンクアドレス:キープアライブとパイプラインリクエスト処理のnginx分析

今回は主にnginxにおけるキープアライブとパイプラインの処理について見ていきます。ここで概念を紹介する必要はありません。 nginx がどのように行うかを直接見てみましょう。 まずキープアライブの処理を見てみましょう。クライアントが明示的に接続ヘッダーを close として指定しない限り、http 1.1 ではキープアライブがデフォルトであることがわかっています。以下は、キープアライブが必要かどうかを判断する nginx のコードです。

{コード>................................................ .. ...

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

voidvoid

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;

        }

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

}

ngx_http_handler(ngx_http_request_t *r) code >
🎜 switch(r->headers_in.connection_type) {🎜🎜 code> <code>case0:🎜🎜//バージョンが 1.0 より大きい場合、デフォルトはキープアライブです🎜🎜 r- >keepalive = (r->http_version > NGX_HTTP_VERSION_10);🎜🎜 break; 🎜🎜 /code><code>caseNGX_HTTP_CONNECTION_CLOSE:​​ode>🎜🎜<code>//接続ヘッダーが close として指定されている場合、キープアライブは必要ありません🎜 🎜 <code>r->keeper code>caseNGX_HTTP_CONNECTION_KEEP_ALIVE:​​ode>🎜🎜<code>コード>break;🎜🎜 コード>}コード>🎜🎜................................ ....🎜🎜}🎜🎜🎜🎜🎜🎜
すると、キープアライブは、現在の http リクエストが実行された直後に現在の接続が閉じられないことを意味することがわかります。そのため、nginx のキープアライブの関連処理は、リクエストをクリーンアップする機能でもあります。 nginx のリクエストをクリーンアップする関数は ngx_http_finalize_request です。この関数では、接続を解放するために ngx_http_finalize_connection が呼び出され、キープアライブの関連する判断がこの関数内にあります。

ngx_http_finalize_connection(ngx_http_request_t *r)

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;

    静的void elseif(r->lingering_close && clcf->lingering_timeout > 0) {

        ngx_http_set_lingering_close(r);

        return;

    }

    ngx_http_close_request(r, 0);

}

🎜{ 🎜🎜🎜🎜 🎜🎜ngx_http_core_loc_conf_t *clcf;🎜🎜🎜🎜 🎜🎜clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);🎜🎜🎜🎜. .................................................................🎜 🎜 🎜🎜//キープアライブが設定されており、タイムアウトが0より大きい場合、キープアライブ処理に入ることがわかります。 🎜🎜🎜🎜 r->キープアライブ🎜🎜🎜 🎜 🎜🎜&& clcf->keepalive_timeout > 🎜 🎜🎜🎜 🎜🎜{🎜🎜🎜🎜 🎜🎜ngx_http_set_keepalive(r);🎜🎜🎜 🎜🎜🎜🎜 🎜🎜} 🎜🎜else🎜🎜if🎜🎜(r->lingering_close && clcf->lingering_timeout > 0) {🎜🎜🎜 🎜 🎜🎜}🎜🎜🎜🎜 🎜🎜ngx_http_close_request(r, 0);🎜🎜🎜🎜 }🎜🎜🎜🎜🎜🎜🎜上記から、キープアライブが ngx_http_set_keepalive を通じて設定されていることがわかります。次に、この関数を詳しく見ていきます。 この関数ではパイプラインリクエストもついでに処理しますので、まずnginxがクライアントから読み込んだデータにさらに多くのデータが含まれている場合、つまりAfterを解析する際にどのようにパイプラインリクエストを区別するのかを一緒に見ていきましょう。現在のリクエストが完了しても、まだデータがいくつかあります。これはパイプライン リクエストとみなされます。 もう 1 つの非常に重要な点は http_connection です。前のブログから、大きなヘッダーを割り当てる必要がある場合は、最初に hc->free から取得され、そうでない場合は作成されてから hc に渡されることがわかります。 -> ;管理が忙しいです。そして、この buf はここで再利用されます。これは、大きな buf が大きい場合、2 回目の再割り当てが必要になるためです。ここで buf を再利用すると、1 つの割り当てが減ります。

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

2 7

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,

                   hc = r->http_connection;sizeof(ngx_buf_t *));

                if(hc->free== NULL) {

                    ngx_http_close_request(r, 0);

                    return;

                }

🎜🎜 🎜b = r->header_in;🎜🎜🎜🎜//通常、header_in を解析した後、pos は last に設定されます。つまり、読み取られたデータは完全な http リクエストであるということは、pos が最後よりも小さい場合、それがパイプライン リクエストである可能性があることを意味します。 🎜🎜🎜🎜 🎜🎜if🎜🎜(b->pos < b->last) {🎜🎜🎜🎜 🎜🎜if🎜🎜(b != c->buffer) {🎜🎜🎜🎜 🎜🎜 *処理を要求します。その後、c-&gtを使用しません。 = NULL) {🎜🎜🎜🎜//large_client_headers の数がわかります 🎜🎜🎜🎜 🎜🎜🎜🎜 cscf->large_client_header_buffers.num * 🎜🎜sizeof🎜🎜(ngx_buf_t *)); 🎜🎜🎜🎜〜🎜🎜

}

//然后清理当前的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的处理。

f = hc->busy[i];

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 }"pipelined request");

#if (NGX_STAT_STUB)

        (void

//その後、現在のリクエストのビジー状態をクリーンアップします

#endif

//设置标记。

        hc->pipeline = 1;

        c->log 1; i++) {"reading client pipelined request line";

//然后扔进post queue,继续进行处理.

        rev->handler = ngx_http_init_request;

        ngx_post_event(rev, &ngx_posted_events);

        return;

    }

🎜🎜 🎜🎜f->pos = f->start;🎜🎜🎜🎜 er_in buf を次回無料で使用します。 🎜🎜🎜🎜 🎜🎜}🎜🎜🎜🎜 🎜🎜}🎜🎜🎜🎜🎜 🎜🎜🎜🎜次の部分は無料リクエストを実行し、キープアライブ タイマーを設定します。🎜🎜
🎜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); 🎜🎜 🎜🎜wev->handler = _ハンドラー;🎜🎜🎜🎜 🎜 🎜🎜🎜次の部分はパイプラインの処理です。🎜🎜
🎜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->🎜🎜ログ🎜🎜, 0、🎜🎜「パイプラインリクエスト」🎜🎜);🎜🎜🎜🎜#if (NGX_STAT_STUB)🎜🎜🎜🎜 _reading, 1);🎜🎜🎜🎜#endif🎜🎜🎜🎜//タグを設定します。 🎜🎜🎜🎜 🎜🎜「クライアントのパイプラインリクエストラインを読み取り中」🎜🎜;🎜🎜🎜🎜//その後、それを投稿キューにスローして処理を続行します 🎜🎜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

2 7

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。

b = c->buffer;

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;

        }

    }

🎜🎜 🎜🎜if🎜🎜(ngx_pfree(c->pool, b->start) == NGX_OK) {🎜 🎜🎜🎜🎜🎜🎜🎜* NGX_HTTP_KEEPALIVE_HANDLERの特別なメモ()that🎜🎜🎜🎜* c-&gt;バッファーのメモリは解放されました🎜🎜🎜🎜🎜🎜🎜🎜🎜🎜} 「🎜🎜🎜🎜🎜}🎜🎜🎜🎜......................................」 ......................................🎜🎜🎜🎜 = 0; i busy[i] = NULL;🎜🎜🎜🎜🎜🎜🎜🎜🎜🎜🎜🎜🎜🎜キープアライブハンドラを設定します。 🎜🎜🎜🎜🎜🎜🎜🎜1🎜🎜2🎜🎜3🎜🎜4🎜🎜5🎜🎜6🎜🎜7🎜🎜8🎜🎜9🎜🎜🎜🎜 🎜🎜//この関数は後で詳しく分析します🎜 🎜🎜0)!= ngx_ok){🎜🎜🎜🎜🎜最後は、ここでは紹介しませんが、次は nginx の tcp プッシュの動作を特別にブログで紹介します。 次に、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;    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的处理。

if(b-> ;pos == NULL) { /*

1

ngx_http_init_request(rev);

size = b->end - b->start;
🎜 * c->buffer のメモリはngx_http_set_keepalive() によって解放されました。🎜🎜 * ただし、c->buffer->start と c->buffer->end は変更されませんでした。🎜🎜 * code> code>🎜🎜<code> * バッファ サイズを維持します。🎜🎜 */🎜🎜 // buf を再割り当てします🎜🎜 b->pos = ngx_palloc(c->pool, size);🎜🎜 if(b->pos == NULL) {🎜🎜 ngx_http_close_connection(c);🎜🎜 b->start = b->pos;🎜🎜 b-> ;last = b->pos;🎜🎜 b->last = b->pos;🎜🎜 /code>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; code>🎜🎜if( n == NGX_AGAIN) {🎜🎜 if(ngx_handle_read_event(rev , 0) != NGX_OK) {🎜🎜 ngx_http_close_connection(c);🎜🎜 } 🎜🎜 return;🎜🎜}🎜🎜🎜🎜🎜🎜🎜🎜🎜最後にデータが読み取れたらリクエスト処理に入ります。 🎜🎜🎜🎜🎜🎜🎜🎜1🎜🎜🎜🎜🎜ngx_http_init_request(rev);🎜🎜🎜🎜🎜🎜最後に、ngx_http_init_request 関数を見てみましょう。今回は主に、パイプラインがリクエストされたときに nginx がリクエストをどのように再利用するかを見ていきます。
ここで hc->busy[0] に注意してください。これがパイプライン リクエストの場合、以前に解析されていないリクエスト header_in を保存することがわかっています。これは、その 2 番目のリクエストを読み取っている可能性があるためです。パイプラインリクエスト。

if(r) {

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.

        //リクエストを取得します。ここでパイプラインにあることがわかりますrequest 、前のリクエストを保存します。sizeof(ngx_http_request_t));

        r->pipeline = hc->pipeline;

//如果nbusy存在

        if(hc->nbusy) {

//则保存这个header_in,然后下面直接解析。

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

        }

     else{

        r = hc->request;sizeof(ngx_http_request_t));

        if(r == NULL) {

            ngx_http_close_connection(c);

            return;

        }

        hc->request = r;

    }

//保存请求

    c->data = r;

// 存在する場合は、前のリクエストを再利用しますリクエスト .

🎜sizeof🎜🎜(ngx_http_request_t));🎜🎜🎜 🎜🎜r->pipeline = hc->pipeline;🎜🎜🎜🎜// nbusy が存在する場合🎜🎜🎜 ️ ;n忙しい) {🎜🎜🎜🎜/ /この header_in を保存し、その直下で解析します。 GR-& gt; header_in = hc-& gt; ビジー 🎜🎜🎜🎜} 🎜🎜else🎜🎜{🎜🎜🎜🎜 🎜🎜r = ngx_pcalloc(c->pool, 🎜🎜sizeof🎜🎜(ngx_http_request_t)); 🎜🎜🎜🎜 _connection(c);🎜🎜🎜🎜 🎜 🎜🎜🎜🎜c-> data = r;🎜🎜🎜 🎜🎜🎜🎜🎜🎜🎜上記のコードより前回のブログと組み合わせると、大きなヘッダーは主にパイプライン用であることがわかります。パイプラインでは、前のリクエストが次のリクエスト ヘッダーをさらに読み込むと、次回解析されるときに最初に割り当てられた client_header_buffer_size を超える可能性があるためです。今回は、大きなヘッダーであるヘッダーを再割り当てする必要があるため、ここでの httpconnection は主にパイプライン状況用であり、キープアライブです。接続がパイプライン リクエストではない場合、メモリを節約するために、前のリクエストは次のようになります。リリースされました。🎜 🎜 上記では、プロセスの側面を含め、nginx のキープアライブおよびパイプライン リクエスト処理の分析を紹介しました。これが、PHP チュートリアルに興味のある友人に役立つことを願っています。 🎜 🎜 🎜
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。