Nginx事件模块学习之定时器

        Nginx中对事件处理的定时器是利用红黑树实现的,下来逐步分析一下nginx如何对定时器实现的。

        首先,Nginx的工作进程是一个无限for循环,主要代码如下:

static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
	……

	// 工作进程初始化调用
    ngx_worker_process_init(cycle, worker);
	……
	// 无限循环
    for ( ;; ) {
		……
        ngx_process_events_and_timers(cycle);

        ……
    }
}

        无限循环中调用的 ngx_process_events_and_timers 就是处理事件的函数。再这之前我们需要了解 ngx_event_timer_init 函数,该函数在工作进程初始化 ngx_worker_process_init 在初始化事件模块时调用的,具体函数如下:

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;
}

        添加事件至事件树上其函数为: ngx_event_add_timer,主要代码如下:

static ngx_inline void ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
{

	// 当前时间 + 定时时间,计算出将来的某一超时时刻
    key = ngx_current_msec + timer;

	……

    ev->timer.key = key;

    // 将事件添加至事件的红黑树上
    ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);

    ……
}

        查询事件树上timer最小的函数为:ngx_event_find_timer,该函数主要查询是不是有事件已经到期或者过期,当过期或者到期都返回 0。具体代码如下:

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);
}

        了解了上述三个函数,下来分析一下工作进程循环里的ngx_process_events_and_timers函数,主要代码如下:

void ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
	……
    // 为epoll的wait提供一个超时时间,以防止epoll的wait阻塞时间太长
	timer = ngx_event_find_timer();
    ……

	// 调用 epoll_wait 去监听并处理读写事件
    (void) ngx_process_events(cycle, timer, flags);

    ……
    if (delta) {
        ngx_event_expire_timers();
    }
	……
}

        上述代码中每次for循环开始都会查找事件树中最小超时事件的时间,将该时间作为epoll_wait函数的超时时间,以防止epoll_wait函数阻塞时间过长。后续的 delta 变量为执行ngx_event_expire_timers函数的时间,如果该时间超过1ms则事件树上有可能有事件到期,则遍历事件树执行到期时间的handler并移除该过期事件,函数为ngx_event_expire_timers,主要代码如下:

void ngx_event_expire_timers(void)
{
	……
    for ( ;; ) {
		……
		
		// 找出最小key值的
        node = ngx_rbtree_min(root, sentinel);

		// 如果最小的key对于的事件没过期则返回 
        if ((ngx_msec_int_t) (node->key - ngx_current_msec) > 0) {
            return;
        }

        ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));
		
		……
		// 删除刚才找出的事件节点
        ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);

		……
		// 执行事件的回调函数
        ev->handler(ev);
    }
}

        执行完ngx_event_expire_timers后一次for循环就算是执行完了,后续一直循环执行就可以。至此,nginx的工作进程定时器处理事件的也算是整个完了。

你可能感兴趣的:(Nginx,nginx,定时器)