如何优雅的关闭关闭这个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); }