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);
}
http 怎样关闭的更多相关文章
- 如何远程关闭一个ASP.NET Core应用?
在<历数依赖注入的N种玩法>演示系统自动注册服务的实例中,我们会发现输出的列表包含两个特殊的服务,它们的对应的服务接口分别是IApplicationLifetime和IHostingEnv ...
- CentOS7使用firewalld打开关闭防火墙与端口(转载)
1.firewalld的基本使用 启动: systemctl start firewalld 查看状态: systemctl status firewalld 停止: systemctl disabl ...
- RMS Server打开或关闭日志记录
原文: https://technet.microsoft.com/zh-cn/library/cc732758 在 Active Directory Rights Management Servic ...
- Stack Overflow 排错翻译 - Closing AlertDialog.Builder in Android -Android环境中关闭AlertDialog.Builder
Stack Overflow 排错翻译 - Closing AlertDialog.Builder in Android -Android环境中关闭AlertDialog.Builder 转自:ht ...
- centos6和centos7防火墙的关闭
CentOS6.5查看防火墙的状态: [zh@localhost ~]$service iptable status 显示结果: [zh@localhost ~]$service iptable st ...
- ASP.NET Aries 2.0 发布(原来的源码SVN已关闭,开源源码已迁移到GitHub)
主要更新: 1:增加子目录部署支持. 2:增加Taurus.MVC支持. 3:优化及Bug修复. 1:增加子目录部署支持: 其实在重写Aries框架的时候,我是去掉了目录部署功能的,主要是为了加快Ar ...
- 【已解决】Https请求——基础连接已经关闭 发送时发生错误
本人在做商用项目的推送消息功能时,借助第三方推送服务.这里避免有打广告的嫌疑,就不报名字了.由于是通过调用API接口,所以Post方法是自己写的,但是在开发环境是可以正常推送的,但是一上线就出各种问题 ...
- 2000条你应知的WPF小姿势 基础篇<40-44 启动关闭,Xaml,逻辑树>
在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师.最为出色的是他维护了两个博客:2,000Things You Should Know About C# 和 2,0 ...
- 未关闭InputStream 引起的血案
下面的方法是从aws s3 读取文件对象下载到本地 public int downloadFile(HttpServletResponse httpResponse, String storePath ...
- SQL Server 阻止了对组件“xp_cmdshell”的 过程“sys.xp_cmdshell”的访问,因为此组件已作为此服务器安全配置的一部分而被关闭。
今天在创建数据库的时候突然发现,xp_cmdshell的存储过程不能用了,网上一搜,发现大部分都是只关闭安全配置,然后就有了下文 代码:具体的看注释,值得一提的是==>reconfigure w ...
随机推荐
- Linux给特定进程单独指定DNS
Linux本身只能通过/etc/resolv.conf设置全系统的DNS.这里有一种给特定进程单独设置DNS的方法,通过免root的mount namespace达成.使用脚本只需要一条简洁的命令就可 ...
- day69:Vue:组件化开发&Vue-Router&Vue-client
目录 组件化开发 1.什么是组件? 2.局部组件 3.全局组件 4.父组件向子组件传值 5.子组件往父组件传值 6.平行组件传值 Vue-Router的使用 Vue自动化工具:Vue-Client 组 ...
- 落地Azure CosmosDb的一个项目分享
我们遇到了什么? 我们有这么一个业务场景,就是某供应商会去爬取某些数据,爬到后会发到一个FTP上,然后我们定时去获取这些数据 这个数据有大有小,小的30多M数据量百万级,大的数据量能到数百M上千万数据 ...
- HTML5/HTML 4.01/XHTML 元素和有效的 DTD
HTML5/HTML 4.01/XHTML 元素和有效的 DTD 下面的表格列出了所有的 HTML5/HTML 4.01/XHTML 元素,以及它们会出现在什么文档类型 (DTD) 中: 标签 HTM ...
- NCEP数据资料获取 地面抬升指数
先放上数据地址:https://www.esrl.noaa.gov/psd/data/gridded/data.ncep.reanalysis.surface.html 美国国家环境预报中心(NCEP ...
- Signal 第一个简单Demo
最简单的聊天室功能 1.用 VS 2013 创建一个 MVC 4 (MVC 5 也类似)项目 1.1 选择模板为 基本 2.用 NuGet 安装 SignalR 3安装完成,我们来添加一个叫 MyHu ...
- 类型转化 - js中的骚操作
Number Number() 把字符串数字转化成数字类型,布尔类型也可以转化 parseInt parseInt() 字符串数字转化成数字类型,当布尔类型不可以(NaN),但该函数可以把数字开头的数 ...
- B树摘要
BTree 以下内容是根据<算法导论>摘要而来,由于国内书籍对B树的定义是以阶来定义,而<算法导论>中使用的是最小度来定义,并且节点中关键字个数也不相同,在翻看网上博客时,产生 ...
- Javascript中this作用域以及bind方法的重写
这是一个最近遇到的笔试题,出于尊重,不会说出该公司的名字,源于自身比较少,笔试题是将bind方法用ES3重写,使用bind这个方法,导致一时半会懵了,只记得bind可以改变this的作用域. 作为查漏 ...
- short i =1; i=i+1与short i=1; i+=1的区别
很典型的一到JAVA 基础面试题,上次面试遇到的,现在记录一下. short i =1; i=i+1;short i=1;i+=1;这两有什么区别呢 ?对两个容量不一样的数据类型的变量进行算术运算时, ...