nginx中的定时器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangqi_gsts/article/details/52461031

nginx中到处充斥着定时器,那么nginx中的定时器是怎么来管理的?是不是每个定时都要检查是否超时?要是这样,那效率太低下了,细读源码来看看nginx中的定时器是怎么样的。

首先来一个大概的认识:nginx中的定时器是放在一个全局的叫做ngx_event_timer_rbtree的红黑树中的,通过ngx_add_timer来添加定时器,等超时定时事件执行完后就从红黑树中移除该定时器,所以想要达到循环定时的目的,那就需要每次在执行定时操作后再次通过ngx_add_timer添加到定时列表中。

#define ngx_add_timer        ngx_event_add_timer

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;

    key = ngx_current_msec + timer; /*当前时间 + 定时时间,这个值是固定的,表示将来的某一时刻,作为红黑树中的key*/

    if (ev->timer_set) { /*如果该时间已经设置了定时器*/

        /*
         * Use a previous timer value if difference between it and a new
         * value is less than NGX_TIMER_LAZY_DELAY milliseconds: this allows
         * to minimize the rbtree operations for fast connections.
         */

        diff = (ngx_msec_int_t) (key - ev->timer.key);

        if (ngx_abs(diff) < NGX_TIMER_LAZY_DELAY) { /*如果距离超时时间 < 300ms,此时无需添加新的定时器,用已有的定时器就可以了*/
            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                           "event timer: %d, old: %M, new: %M",
                            ngx_event_ident(ev->data), ev->timer.key, key);
            return;
        }

        ngx_del_timer(ev); /*删除已有的定时器*/
    }

    ev->timer.key = key;

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                   "event timer add: %d: %M:%M",
                    ngx_event_ident(ev->data), timer, ev->timer.key);

    ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer); /*新节点插入红黑树,注意的是我们并没有插入整个event,而是插入了ev->timer*/

    ev->timer_set = 1;/*状态置为1*/
}

再来看超时检测

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_time */

        if ((ngx_msec_int_t) (node->key - ngx_current_msec) > 0) { /*还没超时*/
            return;
        }

        ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));/*由于在插入红黑树时插入的是ev->timer,所以这个地方需要偏移来得到完整的event*/

        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;/*状态置为 0*/
        ev->timedout = 1; /*设为超时*/

        ev->handler(ev);/*执行定时操作*/
    }
}

注意的地方:

由于在红黑树总保持的是ev->timer,而不是整个完整的event,所以想要获取完整的event就需要偏移。

我们知道,红黑树的优点是有良好的插入和查询性能,那么这里的红黑树是否能用小根堆来实现呢?



阅读更多

没有更多推荐了,返回首页