http 怎样关闭
  WNX8ZrUuErM5 2023年11月02日 104 0

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

 

http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
WNX8ZrUuErM5
作者其他文章 更多