nginx中的定时器
服务器中定时器是重要的组成部分,nginx也不例外,nginx将定时器作为一种事件类型来进行处理。在nginx中利用红黑树来存储定时器,至于为什么选择红黑树,个人认为主要是为了兼顾查找、插入、删除的效率而选取的折中方案。
1 定时器的初始化、添加、删除
初始化定时器,即创建一颗红黑树
ngx_int_t ngx_event_timer_init(ngx_log_t *log)
{
ngx_rbtree_init(&ngx_event_timer_rbtree, &ngx_event_timer_sentinel,
ngx_rbtree_insert_timer_value);
return NGX_OK;
}
添加定时事件
static ngx_inline void ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
{
ngx_msec_t key;
ngx_msec_int_t diff;
//超时的时间为当前时间加上timer
key = ngx_current_msec + timer;
...
//设置时间的定时器超时时间
ev->timer.key = key;
...
//将事件的超时时间插入到红黑树中
ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);
ev->timer_set = 1;
}
删除定时事件
static ngx_inline void ngx_event_del_timer(ngx_event_t *ev)
{
...
ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);
...
ev->timer_set = 0;
}
2 定时器的触发
nginx中定时器的触发方式有两种,一种是利用信号,另一种是利用epoll的超时返回机制。
信号触发
利用系统函数设置定时事件的到期时间,若到达时间点后,将会产生一个SIGALRM信号,当前的epoll将会中断,转去处理定时事件。系统默认不会采用这种方式,若需要使用这种定时触发方式,在配置文件设置timer_resolution指令即可。
if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
struct sigaction sa;
struct itimerval itv;
ngx_memzero(&sa, sizeof(struct sigaction));
sa.sa_handler = ngx_timer_signal_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGALRM, &sa, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigaction(SIGALRM) failed");
return NGX_ERROR;
}
itv.it_interval.tv_sec = ngx_timer_resolution / 1000;
itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;
itv.it_value.tv_sec = ngx_timer_resolution / 1000;
itv.it_value.tv_usec = (ngx_timer_resolution % 1000 ) * 1000;
if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"setitimer() failed");
}
}
利用epoll的超时机制
该机制是利用epoll超时返回的机制来实现的,当超时返回时,首先进入**ngx_event_expire_timers()**来检查是否有超时事件,若有超时,将其移出红黑树,设置超时标记,并调用回调函数进行处理即处理。在处理完定时器事件后再转去处理其他事件。利用超时机制最关键的一点是如何设置超时时间timer,nginx将定时器中最早到期时间减去当前时间的值作为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);
}
}
timer = ngx_event_find_timer();
ngx_msec_t ngx_event_find_timer(void)
{
ngx_msec_int_t timer;
ngx_rbtree_node_t *node, *root, *sentinel;
if (ngx_event_timer_rbtree.root == &ngx_event_timer_sentinel) {
return NGX_TIMER_INFINITE;
}
root = ngx_event_timer_rbtree.root;
sentinel = ngx_event_timer_rbtree.sentinel;
node = ngx_rbtree_min(root, sentinel);
//最早到期时间减去当前时间
timer = (ngx_msec_int_t) (node->key - ngx_current_msec);
return (ngx_msec_t) (timer > 0 ? timer : 0);
}