Chinaunix首页 | 论坛 | 博客
  • 博客访问: 763665
  • 博文数量: 116
  • 博客积分: 923
  • 博客等级: 准尉
  • 技术积分: 1635
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-06 21:43
个人简介

一直帮老板搬运代码!!!

文章分类
文章存档

2013年(47)

2012年(69)

分类: LINUX

2013-01-28 15:25:23

nginx的超时处理

2011年9月9日 1,257 次浏览

请关注最新修正合订:
这一系列的文章还是在09年写的,存在电脑里很久了,现在贴出来。顺序也不记得了,看到那个就发那个吧,最近都会发上来。欢迎转载,但请保留链接:,谢谢。
对 于是否存在有超时事件的处理很巧妙。首先,nginx利用红黑树来组织那些等待处理的并且需要关注其是否超时的事件对象(以下称该红黑树为事件计时红黑 树:event_timer_rbtree);其次,nginx提供了两种方案来检测那些等待处理的事件对象是否已经超时,一种为常规的定时检测机制,也 就是设置定时器,每过一定的时间就对红黑树管理的所有事件对象进行一次超时检测;另一种是过距离当前最快发生超时的事件对象的时间就进行一次超时检测,这 样叙述可能不是很清楚,看完下面的介绍,读者就会理解。

我们已经知道nginx把事件封装在一个名为ngx_event_s的结构体内,而该结构体有几个字段与本节讲的nginx超时处理相关。
unsigned         timedout:1;
unsigned         timer_set:1;
ngx_rbtree_node_t   timer;
timedout域字段,用于标识该当前事件是否已经超时,0为没有超时。
timer_set域字段,用于标识该当前事件是否已经加入到红黑树,需要对其是否超时做监控,0为没有加入。
tmer字段,很容易看出属于红黑树节点类型变量,红黑树就是通过该字段来组织事件对象。

nginx设置了两个全局变量以便在程序的任何地方都能快速的访问到事件计时红黑树:
ngx_thread_volatile ngx_rbtree_t  ngx_event_timer_rbtree;
static ngx_rbtree_node_t  ngx_event_timer_sentinel;
它们都定义在源文件ngx_event_timer.c内,结构体ngx_rbtree_s类型的全局变量ngx_event_timer_rbtree 封装了事件计时红黑树树结构,而ngx_event_timer_sentinel属于红黑树节点类型变量,在红黑树的操作过程中当作哨兵使用,同时它是 static的,所以作用域仅限于模块内。

在nginx事件模块ngx_event_core_module的进程初始化回调函数ngx_event_process_init内将通过ngx_event_timer_init函数进行事件计时红黑树的初始化工作,初始化之后的事件计时红黑树图示描述如下所示:

当nginx需要对某些事件进行超时监控时,就将该事件加入到该事件计时红黑树。那在哪些地方需要进行这种超时监控处理呢?这样的地方很多,举个具 体例子来说,当客户端对nginx发起请求连接时,nginx就会建立对应的连接对象connection并要读取客户端请求的头部信息,而读取这个头部 信息并不是不限时的,如果在一个有限的时间内没有读取到头部信息或头部信息不完整,那么nginx就应该认为这是一个非法的请求,返回错误信息 (”Request time out” (408)),从而释放相应的资源。很显然,这儿就是一个需要进行超时处理的地方。因此,在nginx源码的请求连接初始化函数 ngx_http_init_connection内,可以看到这么一行代码:
ngx_add_timer(rev, c->listening->post_accept_timeout);
这就是将rev事件(用于触发读取头部信息事件)对象加入到nginx的事件计时红黑树内进行超时监控管理,同时给它指定的超时时限为c->listening->post_accept_timeout。
类似的需要超时处理的地方还有很多,比如请求POST信息读取超时、连接keepalive超时等等。

ngx_add_timer函数完 成将一个事件对象加入到事件计时红黑树的功能,这种加入是间接性的,根据前面的介绍可知,每个事件对象都有一个tmer字段,并且其为 ngx_rbtree_node_t类型变量,加入到事件计时红黑树的就是这个字段,而非事件对象本身。当然,利用offsetof宏也可以通过该 tmer字段快速方便的找到其所在的对应事件对象,所以并不用为这种设计而担心。

具有四个节点的红黑树图示描述如下所示,从该图中可以看到两点:第一,可以通过全局变量ngx_event_timer_rbtree.root访 问到该事件计时红黑树;第二,从该红黑树树根从左或从右遍历下去,最后都将到达全局变量ngx_event_timer_sentinel指定的末端树节 点,这也是前面提到的将ngx_event_timer_sentinel当作哨兵节点。

通过事件计时红黑树,nginx对那些需要关注其是否超时的事件对象就有了统一的管理,nginx可以选择在合适的时机对事件计时红黑树管理的事件进行一次超时检测, 对于超时了的事件对象就进行相应的处理。前面曾提到过nginx选择合适时机的方案有两种,下面就来分别介绍,不过在此之前,我们需要了解nginx工作 进程执行的大体流程,如下图所示,该图中给出了与本节内容相关较为紧密的相关执行步骤,省掉了本节不关心的内容,以便理解。

简化的nginx工作进程执行流程就是一个大的for循环,该循环的执行路径如上图中的②③④所示,其中的函数ngx_process_events_and_timers调用是我们关注的重点,nginx的主要工作也是在该函数内完成的。

在函数ngx_process_events_and_timers开始,将首先判断ngx_timer_resolution是否为非零值,这个判断就决定了nginx的超时检测方案,反应到代码里就是根据判断结果设置对应的变量timer和flags。
变量flags的作用很明显,用作标识;而变量timer表示下面的IO复用模型进行监听发生事件的最长等待时间:1,如果变量ngx_timer_resolution非零,那么变量timer将被设置为NGX_TIMER_INFINITE, 即IO复用模型将一直监听等待,直到有事件发生为止。如果有IO事件发生,那么nginx将走路径⑻⑼⒃,注意到前面将变量flags设置为0就很容易理 解这点。当然,就算没有IO事件发生,nginx也不会让工作进程无限制的阻塞在这个事件监听等待处,IO复用模型会在最多等待 ngx_timer_resolution时间后收到定时器中断事件而停止等待,从而继续下面的处理,即经过⑻⑽⒀⒁进行一次超时检测。这也就是超时检测 方案一,即大概每过ngx_timer_resolution时间就进行一次超时检测。2,如果变量ngx_timer_resolution值为零,那 么变量timer将被设置为最快发生超时的事件对象的超时时刻与当前时刻的时间差,举个例子来说,比如事件计时红黑树管理着三个事件a、b、c,它们分别 将在5、6、7秒后超时,那么距离当前最快发生超时的就是事件a,而事件a的超时时刻与当前时刻的时间差为5,因此变量timer就将被设置5。 timer值的具体计算实现在函数ngx_event_find_timer内,该函数从事件计时红黑树内找到key值最小的节点,然后用该节点的key值减去当前时刻(ngx_current_msec) 即得到预期的timer值。预期的timer值可能为负数,这表示已经有事件超时了,因此直接将timer值设置为0,那么接下来的IO复用模型查询IO 事件时会立即返回,以便能马上处理这些超时事件。路径⑻⑼⑿⒁和⑾⒁分别表示了超时检测方案二的可能执行过程,因此不管是哪一条路径都会进行超时检测。

比较两种超时检测方案可以看到,方案一简单直观、容易理解,但有可能导致一些超时事件得不到及时的处理;方案二避免了方案一的缺点,它能对超时事件 做出及时的处理,然而,如果合理设置方案一中的ngx_timer_resolution值,那么超时事件的延迟处理并不会造成多大问题。另外,相对于方 案一来说,方案二频繁的调用ngx_time_update函数也将会有一定的性能损耗。

由于nginx利用红黑树来组织管理超时事件,因此检测是否有事件超时并不需要遍历所有的事件对象,而直接找到最近的即将超时的事件对象,判断其是 否超时,如果超时则进行相应的超时处理,处理完了再判断第二近的即将超时的事件对象,如此反复,直到遇到某个事件还未超时或所有事件都超时并已处理就结束 检测。这就是超时检测与处理函数ngx_event_expire_timers的大致逻辑,当然还有一些其它额外的动作,比如将超时了的事件对象的相关 字段置位、超时处理、事件对象移出事件计时红黑树等等。


该变量对应client_header_timeout配置指令,即可由用户设定,默认情况下是60秒
事实上它是个宏:#define ngx_add_timer ngx_event_add_timer
函数ngx_event_expire_timers实现这个功能
用户可通过timer_resolution指令设置该变量的值,如果用户未设置那么其默认就是初始值0,因为它是一个全局变量,具体请参考其它章节
#define NGX_TIMER_INFINITE  (ngx_msec_t) -1
key值记录的就是事件的超时时刻,key值最小的节点表示的也就是距离当前最快发生超时的事件
该值并不是实时的,因此和精确的当前时刻会有一些偏差
该函数具体实现请参考其它章节

阅读(5352) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~