Nginx实现了自己的定时器触发机制,与内核无关。用于弥补当内核调用超时等状况下,系统可以主动触发事件。理解定时器主要有两个方面,一个方面是时间管理,另一个方面是定时器的实现方式。
1.时间管理
定时间首先要弄清楚时间管理,Nginx使用全局变量用于缓存时间,这样在获取事件的时候只需要直接读取几个缓存变量就可以了,达到提高了执行效率。缓存的时间更新由ngx_epoll_process_events方法执行,当flags参数中有NGX_UPDATE_TIME标志位或者ngx_event_time_alarm标志位为1时,就会调用ng_time_update方法更新缓存时间。缓存事件的精度有两种控制方式,一种是主动更新,通过调用ngx_epoll_process_events时触发,另一种时被动更新,通过配置文件设置最小更新时间(timer_resolution),然后由settimer系统调用告诉内核定时更新。除此之外,Nginx还有很多通过程序逻辑控制事件更新的辅助方式,弥补以上两种更新的不足。
2.定时器的实现
知道了定时器的时间管理后再理解定时器的实现就方便多了。Nginx的定时器是一个红黑树,ngx_event_timer_rbtreed定义定时器,ngx_event_timer_sentimel作呕红黑树的哨兵节点,代码如下
ngx_rbtree_t ngx_event_timer_rbtree;
static ngx_rbtree_node_t ngx_event_timer_sentinel;
定时器的实现共有5个操作方法,其中ngx_event_expire_timers方法作用是触发所有超时的事件,ngx_event_expire_timers会循环调用所有满足条件的事件的handler方法。调用频率参考ngx_event_find_timer方法, 这个方法会决定下一个最近的超时事件多久后可能会发生。
void
ngx_event_expire_timers(void)
{
ngx_event_t *ev;
ngx_rbtree_node_t *node, *root, *sentinel;/*定时器事件节点*/
sentinel = ngx_event_timer_rbtree.sentinel;/*哨兵节点*/
/*轮训满足条件的事件,轮训频率依赖操作系统的时钟频率*/
for ( ;; ) {
root = ngx_event_timer_rbtree.root;
if (root == sentinel) {
return;
}
node = ngx_rbtree_min(root, sentinel);
/* node->key > ngx_current_msec */
if ((ngx_msec_int_t) (node->key - ngx_current_msec) > 0) {
return;
}
ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"event timer del: %d: %M",
ngx_event_ident(ev->data), ev->timer.key);
ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);
#if (NGX_DEBUG)
ev->timer.left = NULL;
ev->timer.right = NULL;
ev->timer.parent = NULL;
#endif
ev->timer_set = 0;
ev->timedout = 1;
ev->handler(ev);/*调用满足条件的事件的handler方法*/
}
}