本博客已迁移到 wenda.dreamshare.in 欢迎访问!

NGINX 定时器

写在前面

写NGINX系列的随笔,一来总结学到的东西,二来记录下疑惑的地方,在接下来的学习过程中去解决疑惑。

也希望同样对NGINX感兴趣的朋友能够解答我的疑惑,或者共同探讨研究。

整个NGINX系列的文章中,我会将我的疑惑用红色标出,希望能遇到前辈在评论中给我解答迷津。

定时器

在介绍定时器之前,先简要说下nginx处理事件的流程和方式。

Worker进程的主要流程:

 

 1 static void
 2 ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data){
 3   for(;;) {
 4   if(ngx_exiting) {}
 5   ngx_process_events_and_timers(cycle);
 6   if (ngx_terminate) {}
 7   if (ngx_quit) {}
 8   if (ngx_reopen) {}
 9 }
10 
11 void
12 ngx_process_events_and_timers(ngx_cycle_t *cycle) {
13   (void) ngx_process_events(cycle, timer, flags);
14 }

 

ngx_process_events调用epoll(linux下)实现事件的处理。

ngx_process_events处理事件有两种方式:一是直接调用处理函数处理,二是将事件放到post队列中,函数返回后再处理队列中的事件。

在使用了 NGX_POST_EVENTS标记时,ngx_process_events不直接处理事件,将事件放到Post队列中,待函数返回,再在队列中取出事件处理。

因为调用ngx_process_events会加锁(为什么加锁?),函数返回后,将锁释放再处理事件,可以减少锁的占用时间。

 

以上是nginx处理事件的大体方式,下面介绍nginx中定时器的实现。

Nginx定时器使用红黑树组织(为什么使用红黑树,红黑树效率高到哪?可以研究下)存储,这个不多说。

Nginx定时器的触发有两种方式,第一种是设置时间信号。

ngx_event_process_init函数中 ,设置了时间信号,每隔固定时间触发,时间信号的处理函数,只是设置ngx_event_timer_alarm = 1,但他会中断ngx_process_eventsepoll_wait的处理,epoll_wait返回后,调用ngx_time_update更新时间,接着返回到函数ngx_process_events_and_timers中处理,ngx_process_events_and_timers中,会调用ngx_event_expire_timers,查询超时的事件并处理。

但这种方式有个问题,如果事件信号是在处理IO事件时(epoll_wait调用之后)发生的,那么定时器的查询遍历,只能到下一次epoll_wait调用时才会处理,如果这时有IO事件发生,那么epoll_wait可以立即返回,然后因为上次信号发生已经置ngx_event_timer_alarm = 1,可以立即更新时间,ngx_process_events返回后可以处理定时器事件。但如果没有IO事件发生,epoll_wait会阻塞到下次时间信号到来,然后处理定时器事件,这样岂不大大降低了定时器的精确度。这块nginx怎么处理的?

 

另外每隔固定时间(具体设置的时间信号的时间)才更新时间值,甚至可能是两倍时间信号的时间才更新时间值,那么代码中在插入的定时器,实际触发时间和理论时间就会有这么大的误差。是不是这样呢?

 

Nginx定时器的第二种触发方式是利用epoll_wait的超时。

每次在调用epoll_wait之前,nginx都会取得下一个最小(最早要触发)的定时器的时间值,然后拿这个值作为epoll_wait的超时时间。这样epoll_wait在返回后就可以处理超时事件了。既可以在频繁IO的情况下处理超时,又可以在IO少量的情况下处理超时。

这种方式epoll_wait返回后,都会先更新时间,这样epoll_wait返回后,在IO事件的处理代码中加入定时器,误差不会太大,因为时间刚刚被更新。

但这个方法的问题是,IO频繁的情况下,也会频繁更新时间,是否会影响性能?

这两种方式各自的优缺点是哪些呢?

posted @ 2014-06-17 14:54  北半球的天空  阅读(1970)  评论(1编辑  收藏  举报