http 怎樣關閉


  如何優雅的關閉關閉這個fd , 如果只是一個簡單的fd 直接調用close 就行, 但是如果要是一個框架 那就接到 資源回收復用 內存泄漏等問題;

來看看 ngx 是用怎樣的思路處理 事務結束動作;

  每個HTTP請求都有一個引用計數,每派生出一種新的會獨立向事件收集器注冊事件的動作時(如ngx_http_read_ client_request_body方法或者ngx_http_subrequest方法),都會把引用計數加1,這樣每個動作結束時都通過調用ngx_http_finalize_request方法來結束請求,而ngx_http_finalize_request方法實際上卻會在引用計數減1后先檢查引用計數的值,如果不為O是不會真正銷毀請求的。

 

void
ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) 
{//subrequest注意ngx_http_run_posted_requests與ngx_http_postpone_filter ngx_http_finalize_request配合閱讀
    ngx_connection_t          *c;
    ngx_http_request_t        *pr;
    ngx_http_core_loc_conf_t  *clcf;

    c = r->connection;

    ngx_log_debug7(NGX_LOG_DEBUG_HTTP, c->log, 0,
                   "http finalize request rc: %d, \"%V?%V\" a:%d, c:%d, b:%d, p:%p",
                   rc, &r->uri, &r->args, r == c->data, r->main->count, (int)r->buffered, r->postponed);

    /*
    NGX_DONE參數表示不需要做任何事,直接調用ngx_http_finalize_connection方法,之后ngx_http_finalize_request方法結束。當某一種動作
(如接收HTTP請求包體)正常結束而請求還有業務要繼續處理時,多半都是傳遞NGX_DONE參數。這個ngx_http_finalize_connection方法還會去檢
查引用計數情況,並不一定會銷毀請求。
     */
    if (rc == NGX_DONE) {
        ngx_http_finalize_connection(r);  
        return;
    }

    if (rc == NGX_OK && r->filter_finalize) {
        c->error = 1;
    }

    /*
   NGX_DECLINED參數表示請求還需要按照11個HTTP階段繼續處理下去,這時需要繼續調用ngx_http_core_run_phases方法處理請求。這
一步中首先會把ngx_http_request_t結構體的write—event handler設為ngx_http_core_run_phases方法。同時,將請求的content_handler成員
置為NULL空指針,它是一種用於在NGX_HTTP_CONTENT_PHASE階段處理請求的方式,將其設置為NULL足為了讓ngx_http_core_content_phase方法
可以繼續調用NGX_HTTP_CONTENT_PHASE階段的其他處理方法。
     */
    if (rc == NGX_DECLINED) {
        r->content_handler = NULL;
        r->write_event_handler = ngx_http_core_run_phases;
        ngx_http_core_run_phases(r);
        return;
    }

    /*
    檢查當前請求是否為subrequest子請求,如果是子請求,那么調用post_subrequest下的handler回調方法。subrequest的用法,可以看
    到post_subrequest正是此時被調用的。
     */  /* 如果當前請求是一個子請求,檢查它是否有回調handler,有的話執行之 */  
    if (r != r->main && r->post_subrequest) {//如果當前請求屬於某個原始請求的子請求
        rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc); //r變量是子請求(不是父請求)
    }

    if (rc == NGX_ERROR
        || rc == NGX_HTTP_REQUEST_TIME_OUT
        || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST
        || c->error)
    {
        //直接調用ngx_http_terminate_request方法強制結束請求,同時,ngx_http_finalize_request方法結束。
        if (ngx_http_post_action(r) == NGX_OK) {
            return;
        }

        if (r->main->blocked) {
            r->write_event_handler = ngx_http_request_finalizer;
        }

        ngx_http_terminate_request(r, rc);
        return;
    }

    /*
    如果rc為NGX_HTTP_NO_CONTENT、NGX_HTTP_CREATED或者大於或等於NGX_HTTP_SPECIAL_RESPONSE,則表示請求的動作是上傳文件,
    或者HTTP模塊需要HTTP框架構造並發送響應碼大於或等於300以上的特殊響應
     */
    if (rc >= NGX_HTTP_SPECIAL_RESPONSE
        || rc == NGX_HTTP_CREATED
        || rc == NGX_HTTP_NO_CONTENT)
    {
        if (rc == NGX_HTTP_CLOSE) {
            ngx_http_terminate_request(r, rc);
            return;
        }

        /*
            檢查當前請求的main是否指向自己,如果是,這個請求就是來自客戶端的原始請求(非子請求),這時檢查讀/寫事件的timer_set標志位,
            如果timer_set為1,則表明事件在定時器申,需要調用ngx_del_timer方法把讀/寫事件從定時器中移除。
          */
        if (r == r->main) {
            if (c->read->timer_set) {
                ngx_del_timer(c->read, NGX_FUNC_LINE);
            }

            if (c->write->timer_set) {
                ngx_del_timer(c->write, NGX_FUNC_LINE);
            }
        }

        /* 設置讀/寫事件的回調方法為ngx_http_request_handler方法,這個方法,它會繼續處理HTTP請求。 */
        c->read->handler = ngx_http_request_handler;
        c->write->handler = ngx_http_request_handler;

    /*
      調用ngx_http_special_response_handler方法,該方法負責根據rc參數構造完整的HTTP響應。為什么可以在這一步中構造這樣的響應呢?
      這時rc要么是表示上傳成功的201或者204,要么就是表示異步的300以上的響應碼,對於這些情況,都是可以讓HTTP框架獨立構造響應包的。
      */
        ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc));
        return;
    }

    if (r != r->main) { //子請求
         /* 該子請求還有未處理完的數據或者子請求 */  
        if (r->buffered || r->postponed) { //檢查out緩沖區內是否還有沒發送完的響應
             /* 添加一個該子請求的寫事件,並設置合適的write event hander, 
               以便下次寫事件來的時候繼續處理,這里實際上下次執行時會調用ngx_http_output_filter函數, 
               最終還是會進入ngx_http_postpone_filter進行處理,在該函數中不一定會把數據發送出去,而是掛接到postpone鏈上,等高優先級的子請求先發送 */ 
            if (ngx_http_set_write_handler(r) != NGX_OK) { 
                ngx_http_terminate_request(r, 0);
            }

            return;
        }

        /*
            由於當前請求是子請求,那么正常情況下需要跳到它的父請求上,激活父請求繼續向下執行,所以這一步首先根據ngx_http_request_t結
        構體的parent成員找到父請求,再構造一個ngx_http_posted_request_t結構體把父請求放置其中,最后把該結構體添加到原始請求的
        posted_requests鏈表中,這樣ngx_http_run_posted_requests方法就會調用父請求的write_event_handler方法了。
        */
        pr = r->parent;

        /*
          sub1_r和sub2_r都是同一個父請求,就是root_r請求,sub1_r和sub2_r就是ngx_http_postponed_request_s->request成員
          它們由ngx_http_postponed_request_s->next連接在一起,參考ngx_http_subrequest

                          -----root_r(主請求)     
                          |postponed
                          |                next
            -------------sub1_r(data1)--------------sub2_r(data1)
            |                                       |postponed                    
            |postponed                              |
            |                                     sub21_r-----sub22
            |
            |               next
          sub11_r(data11)-----------sub12_r(data12)

     */
        if (r == c->data) { 
        //這個優先級最高的子請求數據發送完畢了,則直接從pr->postponed中摘除,例如這次摘除的是sub11_r,則下個優先級最高發送客戶端數據的是sub12_r

            r->main->count--; /* 在上面的rc = r->post_subrequest->handler()已經處理好了該子請求,則減1 */
            r->main->subrequests++;

            if (!r->logged) {

                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

                if (clcf->log_subrequest) {
                    ngx_http_log_request(r);
                }

                r->logged = 1;

            } else {
                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                              "subrequest: \"%V?%V\" logged again",
                              &r->uri, &r->args);
            }

            r->done = 1; /* 該子請求的handler已經處理完畢 */
             /* 如果該子請求不是提前完成,則從父請求的postponed鏈表中刪除 */  
            if (pr->postponed && pr->postponed->request == r) {
                pr->postponed = pr->postponed->next;
            }

            /* 將發送權利移交給父請求,父請求下次執行的時候會發送它的postponed鏈表中可以 
               發送的數據節點,或者將發送權利移交給它的下一個子請求 */ 
            c->data = pr;
        } else {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                           "http finalize non-active request: \"%V?%V\"",
                           &r->uri, &r->args);
            /* 到這里其實表明該子請求提前執行完成,而且它沒有產生任何數據,則它下次再次獲得 
               執行機會時,將會執行ngx_http_request_finalzier函數,它實際上是執行 
               ngx_http_finalize_request(r,0),也就是什么都不干,直到輪到它發送數據時, 
               ngx_http_finalize_request 函數會將它從父請求的postponed鏈表中刪除 */  
            r->write_event_handler = ngx_http_request_finalizer; //也就是優先級低的請求比優先級高的請求先得到后端返回的數據,

            if (r->waited) {
                r->done = 1;
            }
        }

         /* 將父請求加入posted_request隊尾,獲得一次運行機會,這樣pr就會加入到posted_requests,
         在ngx_http_run_posted_requests中就可以調用pr->ngx_http_run_posted_requests */  
        if (ngx_http_post_request(pr, NULL) != NGX_OK) {
            r->main->count++;
ngx_http_terminate_request方法是提供給HTTP模塊使用的結束請求方法,但它屬於非正常結束的場景,可以理解為強制關閉請求。也就是說,
當調用ngx_http_terminate_request方法結束請求時,它會直接找出該請求的main成員指向的原始請求,並直接將該原始請求的引用計數置為1,
同時會調用ngx_http_close_request方法去關閉請求
 
         

 


            ngx_http_terminate_request(r, 0);
            return;
        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http wake parent request: \"%V?%V\"",
                       &pr->uri, &pr->args);

        return;
    }

    /* 這里是處理主請求結束的邏輯,如果主請求有未發送的數據或者未處理的子請求, 則給主請求添加寫事件,並設置合適的write event hander, 
       以便下次寫事件來的時候繼續處理 */  
       
    //ngx_http_request_t->out中還有未發送的包體,
    //ngx_http_finalize_request->ngx_http_set_write_handler->ngx_http_writer通過這種方式把未發送完畢的響應報文發送出去
    if (r->buffered || c->buffered || r->postponed || r->blocked) { //例如還有未發送的數據,見ngx_http_copy_filter,則buffered不為0

        if (ngx_http_set_write_handler(r) != NGX_OK) { 
            ngx_http_terminate_request(r, 0);
        }

        return;
    }

    if (r != c->data) {
        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                      "http finalize non-active request: \"%V?%V\"",
                      &r->uri, &r->args);
        return;
    }

    r->done = 1;
    r->write_event_handler = ngx_http_request_empty_handler;

    if (!r->post_action) {
        r->request_complete = 1;
    }

    if (ngx_http_post_action(r) == NGX_OK) {
        return;
    }

    /* 到了這里真的要結束請求了。首先判斷讀/寫事件的timer-set標志位,如果timer-set為1,則需要把相應的讀/寫事件從定時器中移除 */

    if (c->read->timer_set) {
        ngx_del_timer(c->read, NGX_FUNC_LINE);
    }

    if (c->write->timer_set) {
        c->write->delayed = 0;
        //這里的定時器一般在ngx_http_set_write_handler->ngx_add_timer中添加的
        ngx_del_timer(c->write, NGX_FUNC_LINE);
    }

    if (c->read->eof) {
        ngx_http_close_request(r, 0);
        return;
    }

    ngx_http_finalize_connection(r);
}

 

 

ngx_http_finalize_connection方法在結束請求時,解決了keepalive特性和子請求的問題  

ngx_http_finalize_request -> ngx_http_finalize_connection

static void
ngx_http_finalize_connection(ngx_http_request_t *r) //ngx_http_finalize_request->ngx_http_finalize_connection
{
    ngx_http_core_loc_conf_t  *clcf;

#if (NGX_HTTP_V2)
    if (r->stream) {
        ngx_http_close_request(r, 0);
        return;
    }
#endif

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    /*
    查看原始請求的引用計數.如果不等於1,則表示還有多個動作在操作着請求,接着繼續檢查discard_body標志位。如果
discard_body為l,則表示正在丟棄包體,這時會再一次把請求的read_event_handler成員設為ngx_http_discarded_request_body_handler方法,
     */
    if (r->main->count != 1) {

        if (r->discard_body) {
            r->read_event_handler = ngx_http_discarded_request_body_handler;
            //將讀事件添加到定時器中,其中超時時間是lingering_timeout配置項。
            ngx_add_timer(r->connection->read, clcf->lingering_timeout, NGX_FUNC_LINE);

            if (r->lingering_time == 0) {
                r->lingering_time = ngx_time()
                                      + (time_t) (clcf->lingering_time / 1000);
            }
        }

        ngx_http_close_request(r, 0);
        return;
    }

    if (r->reading_body) {
        r->keepalive = 0; //使用延遲關閉連接功能,就不需要再判斷keepalive功能關連接了
        r->lingering_close = 1;
    }

    /*
    如果引用計數為1,則說明這時要真的准備結束請求了。不過,還要檢查請求的keepalive成員,如果keepalive為1,則說明這個請求需要釋放,
但TCP連接還是要復用的;如果keepalive為0就不需要考慮keepalive請求了,但還需要檢測請求的lingering_close成員,如果lingering_close為1,
則說明需要延遲關閉請求,這時也不能真的去結束請求,如果lingering_close為0,才真的結束請求。
     */
    if (!ngx_terminate
         && !ngx_exiting
         && r->keepalive
         && clcf->keepalive_timeout > 0) 
         //如果客戶端請求攜帶的報文頭中設置了長連接,並且我們的keepalive_timeout配置項大於0(默認75s),則不能關閉連接,只有等這個時間到后還沒有數據到來,才關閉連接
    {
        ngx_http_set_keepalive(r); 
        return;
    }

    if (clcf->lingering_close == NGX_HTTP_LINGERING_ALWAYS
        || (clcf->lingering_close == NGX_HTTP_LINGERING_ON
            && (r->lingering_close
                || r->header_in->pos < r->header_in->last
                || r->connection->read->ready)))
    {
       /*
        調用ngx_http_set_lingering_close方法延遲關閉請求。實際上,這個方法的意義就在於把一些必須做的事情做完
        (如接收用戶端發來的字符流)再關閉連接。
        */
        ngx_http_set_lingering_close(r);
        return;
    }

    ngx_http_close_request(r, 0);
}

 

 

/*
在引用計數清零時正式調用ngx_http_free_request方法和ngx_http_close_connection(ngx_close_connection) 方法來釋放請求、關閉連接,見ngx_http_close_request,注意和ngx_http_finalize_connection的區別 */ static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_connection_t *c; //引用計數一般都作用於這個請求的原始請求上,因此,在結束請求時統一檢查原始請求的引用計數就可以了 r = r->main; c = r->connection; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http request count:%d blk:%d", r->count, r->blocked); if (r->count == 0) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "http request count is zero"); } r->count--; /* 在HTTP模塊中每進行一類新的操作,包括為一個請求添加新的事件,或者把一些已繹由定時器、epoll中移除的事件重新加入其中,都需要把這個 請求的引用計數加1。這是因為需要讓HTTP框架知道,HTTP模塊對於該請求有獨立的異步處理機制,將由該HTTP模塊決定這個操作什么時候結束,防 止在這個操作還未結束時HTTP框架卻把這個請求銷毀了(如其他HTTP模塊通過調用ngx_http_finalize_request方法要求HTTP框架結束請求),導致 請求出現不可知的嚴重錯誤。這就要求每個操作在“認為”自身的動作結束時,都得最終調用到ngx_http_close_request方法,該方法會自動檢查引用 計數,當引用計數為0時才真正地銷毀請求 由ngx_http_request_t結構體的main成員中取出對應的原始請求(當然,可能就是這個請求本身),再取出count引用計數並減l。 然后,檢查count引用計數是否已經為0,以及blocked標志位是否為0。如果count已經為O,則證明請求沒有其他動作要使用了,同時blocked標 志位也為0,表示沒有HTTP模塊還需要處理請求,所以此時請求可以真正釋放;如果count引用計數大干0,或者blocked大於0,這樣都不可以結 束請求,ngx_http_close_reques't方法直接結束。 */ if (r->count || r->blocked) { return; } //只有count為0才能繼續后續的釋放資源操作 #if (NGX_HTTP_SPDY) if (r->spdy_stream) { ngx_http_spdy_close_stream(r->spdy_stream, rc); return; } #endif #if (NGX_HTTP_V2) if (r->stream) { ngx_http_v2_close_stream(r->stream, rc); return; } #endif ngx_http_free_request(r, rc); ngx_http_close_connection(c); }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM