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); }
原文地址:https://www.cnblogs.com/codestack/p/13513781.html