首页 > Linux, Nginx > nginx中定时器与事件流程

nginx中定时器与事件流程

2015年4月13日

1.nginx中的时间更新

Nginx在内部自己缓存时间,那么这些时间值在什么地方更新呢?

1.1 收到信号时更新时间

1.2 timer_resolution值为0

如果用户没有设置timer_resolution值或者设置为0值,那么每次在轮询事件都会设置NGX_UPDATE_TIME标志,表示轮询结束后要更新时间(以kqueue为例):

轮询时:

1.3 timer_resolution值非0

如果用户设置timer_resolution为非零值,nginx会在定时器触发时更新时间。

在nginx中系统定时器的具体实现有两种,一种是使用setitimer(),另外一种具体事件中模型实现的。

1.3.1使用setitimer()

在事件初始化后使用setitimer(timer_resolution),那么系统会每隔timer_resolution(单位为毫秒)发送SIGALARM信号,信号处理函数:

后续的的轮询会被打断并在结束后会去判断ngx_event_timer_alarm值,如果为1的话会更新时间(以kqueue为例):

1.3.2使用特定的事件模型

对于特定的事件模型,在这里只包括kqueue和eventport。它们内部实现了一个定时器,我们在事件初始化时安装定时器,例如(以kqueue为例):

同时设置还会置上NGX_USE_TIMER_EVENT标志,并且定时器触发时没有回调函数,我们要在事件轮询后判断是否是定时器事件,如果是则要更新时间(以kqueue为例):

 

总结:nginx自身缓存时间。

如果timer_resolution没有设置或设置成了零值:那么nginx为会在每一次事件轮询前设置更新标志NGX_UPDATE_TIME,同时在轮询后更新时间。

如果设置了timer_resolution为非零值:会在定时器触发时更新时间。定时器有两种实现方式:一种是Linux自带的setitimer(),通过回调的方式设置更新标志并中断轮询函数;另一种是事件模型里实现的定时器,它没有回调函数只会在中断轮询函数,此时只要检查事件中有无定时器事件,如果有则更新时间。

另外,在收到信号时会使用信号安全的函数进行时间更新。

附1.没有使用time_resolution的流程

附2.使用time_resolution和setitimer流程

附3.使用time_resolution和模型自带定时器流程

有一种情况要注意:只要没有抢占到accept锁的进程到最多轮询500ms,那么抢到锁的进程则有可能轮询无限直到有事件或者信号通知,nginx有个很重的概念就是只有等所有事件处理完才会继续轮询,所以这样的等待是可以的,同时还可以避免无谓的抢锁消耗。

2.nginx中定时器和事件的主体处理逻辑

对于定时器的概念要区分系统定时器和nginx定时器。

系统定时器是我们之前说的setitimer或者具体模型中实现的底层定时器往往很精确(相对而言),nginx定时器是指nginx为其它模块提供的定时器,往往不是很精确。

系统定时器往往只是中断轮询函数进行时间更新,更新时间的目的就在查找nginx定时器是否到期,所以系统定时器决定了nginx定时器的精确性。nginx的定时器存放在一棵红黑树上,如果没有设置timer_resolution值或为0,那么nginx会在很短的时间内找出最近的定时器做为轮询的时间,在轮询结束后还会查看是否有nginx的定时器在轮询期间过期。

2.1事件处理流程

下面是nginx的定时器和事件处理流程图
event
对应的代码为:

抢占accept的过程,不单单只是抢锁。它的流程是:

当前进程抢到了锁:如果与上次占锁的进程是同一个进程,那么什么也不用处理,置accept_locked_held标志为1即可;如果当前进程不是上一次抢到锁的那个进程,当前进程就应该将所有读事件加入如有的本地套接字。

当前进程没抢到锁:如果与上次占锁的进程是同一进程,那么将所有读事件从本地套接字中删除,accept_locked_held标志为0即可;如果当前进程不是上一次抢到锁的那个进程,那么什么也用干。

 

分类: Linux, Nginx 标签:
本文的评论功能被关闭了.