nginx模块开发-定时器模型

1、基础知识

nginx中有两种事件类型,其中一种的引发点是epoll,也就是通过epoll来获取事件的源头,另外一种就是定时时间。在前面分析HTTP body的代码中就已经遇到过了,例如当nginx获取到一个客户端的连接时,需要对该连接进行初始化,同时还要给这个连接增加一个定时器,如果都超时了请求的数据都还没有接收完整,那么就直接断开这个连接。

这里,nginx中的定时功能并没有采用操作系统提供的定时器,而是自己实现了一个模拟定时器的方法,最核心的就是一颗红黑树(红黑树目前我不了解也暂时不想了解,因此对于细节暂时不想多披露)。

2、数据结构

ngx_connection_s

struct ngx_connection_s {

    void               *data;

    ngx_event_t        *read;    //读事件

    ngx_event_t        *write;   //写事件

 

    。。。

};

struct ngx_event_s

struct ngx_event_s {

    void            *data;           //指向发生当前事件的connection之类的数据

    unsigned         write:1;

    unsigned         accept:1;   //用这个域来标记当前的事件是监听端口的accept事件 

    unsigned         instance:1;

    unsigned         active:1;    //主要是用于表明是否实际将其加入了epoll  

    unsigned         disabled:1;

    unsigned         ready:1;    //判断当前事件是否已经发生了,例如当在epollwait返回后会将其置

    unsigned         oneshot:1;

    unsigned         complete:1;

    unsigned         eof:1;

    unsigned         error:1;

    unsigned         timedout:1;    //是否是超时,一般是expire_timers来执行

    unsigned         timer_set:1;   //是否放入了定时器中

 

    unsigned         delayed:1;

    unsigned         read_discarded:1;

    unsigned         unexpected_eof:1;

    unsigned         deferred_accept:1;

    unsigned         pending_eof:1;

 

#if !(NGX_THREADS)

    unsigned         posted_ready:1;

#endif

 

#if (NGX_WIN32)

    unsigned         accept_context_updated:1;

#endif

 

#if (NGX_HAVE_KQUEUE)

    unsigned         kq_vnode:1;

 

    /* the pending errno reported by kqueue */

    int              kq_errno;

#endif

 

#if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)

    int              available;

#else

    unsigned         available:1;

#endif

 

    ngx_event_handler_pt  handler;   //事件的处理函数  

 

 

#if (NGX_HAVE_AIO)

 

#if (NGX_HAVE_IOCP)

    ngx_event_ovlp_t ovlp;

#else

    struct aiocb     aiocb;

#endif

 

#endif

 

    ngx_uint_t       index;

 

    ngx_log_t       *log;   

 

    ngx_rbtree_node_t   timer;   //对应的红黑树的节点

 

    unsigned         closed:1;

 

    /* to test on worker exit */

    unsigned         channel:1;

    unsigned         resolver:1;

 

#if (NGX_THREADS)

 

    unsigned         locked:1;

 

    unsigned         posted_ready:1;

    unsigned         posted_timedout:1;

    unsigned         posted_eof:1;

 

#if (NGX_HAVE_KQUEUE)

    /* the pending errno reported by kqueue */

    int              posted_errno;

#endif

 

#if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)

    int              posted_available;

#else

    unsigned         posted_available:1;

#endif

 

    ngx_atomic_t    *lock;

    ngx_atomic_t    *own_lock;

 

#endif

 

    /* the links of the posted queue */

    ngx_event_t     *next;

    ngx_event_t    **prev;

 

 

#if 0

 

    /* the threads support */

 

    /*

     * the event thread context, we store it here

     * if $(CC) does not understand __thread declaration

     * and pthread_getspecific() is too costly

     */

 

    void            *thr_ctx;

 

#if (NGX_EVENT_T_PADDING)

 

    /* event should not cross cache line in SMP */

 

    uint32_t         padding[NGX_EVENT_T_PADDING];

#endif

#endif

};

3、全局变量

ngx_event_timer_rbtree

ngx_thread_volatile ngx_rbtree_t  ngx_event_timer_rbtree;

备注:这个是全局红黑树,add_timerdelete_timer都是以此为基础。

ngx_event_timer_sentinel

static ngx_rbtree_node_t          ngx_event_timer_sentinel;

备注:会在find_timerexpire_timer时被用到。

ngx_event_timer_mutex

#if (NGX_THREADS)

ngx_mutex_t  *ngx_event_timer_mutex;

#endif

ngx_current_msec

volatile ngx_msec_t      ngx_current_msec; 

备注:ngx_msec_t为一个无符号的整形,用于代表当前的UTC时间,单位是毫秒, 在函数ngx_time_update()中会更新这个值。

4、操作函数

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

 

#if (NGX_THREADS)

 

    if (ngx_event_timer_mutex) {

        ngx_event_timer_mutex->log = log;

        return NGX_OK;

    }

 

    ngx_event_timer_mutex = ngx_mutex_init(log, 0);

    if (ngx_event_timer_mutex == NULL) {

        return NGX_ERROR;

    }

 

#endif

 

    return NGX_OK;

}

clip_image001[4]

ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)

 //timer就是一个int的值,表示超时的事件,用于表示红黑树节点的key

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;  //现在时间+超时时间=过期时间

    //已经设置了定时器

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

        //2次比较相近,则忽略这次的

        if (ngx_abs(diff) < NGX_TIMER_LAZY_DELAY) { //300

            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;

        }

        //否则,删除old

        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_mutex_lock(ngx_event_timer_mutex);

    //事件的timer域插入到红黑树当中  

    ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);   // ev->timernode

 

    ngx_mutex_unlock(ngx_event_timer_mutex);

 

    ev->timer_set = 1;

}

ngx_event_del_timer(ngx_event_t *ev)

static ngx_inline void

ngx_event_del_timer(ngx_event_t *ev)

{

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,

                   "event timer del: %d: %M",

                    ngx_event_ident(ev->data), ev->timer.key);

 

    ngx_mutex_lock(ngx_event_timer_mutex);

 

    ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);

 

    ngx_mutex_unlock(ngx_event_timer_mutex);

 

#if (NGX_DEBUG)

    ev->timer.left = NULL;

    ev->timer.right = NULL;

    ev->timer.parent = NULL;

#endif

 

    ev->timer_set = 0;

}

ngx_event_find_timer(void)

函数功能:用于获取当前事件红黑树中最小的超时时间,将其提供给epoll,让epollwait在这个时间内唤醒。

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;

    }

 

    ngx_mutex_lock(ngx_event_timer_mutex);

 

    root = ngx_event_timer_rbtree.root;

    sentinel = ngx_event_timer_rbtree.sentinel;

    node =ngx_rbtree_min(root, sentinel);

 

    ngx_mutex_unlock(ngx_event_timer_mutex);

 

    timer = (ngx_msec_int_t) (node->key - ngx_current_msec);

 

    return (ngx_msec_t) (timer > 0 ? timer : 0);

}

ngx_event_expire_timers(void)

函数功能:处理红黑树中的所有超时事件

void

ngx_event_expire_timers(void)

{

    ngx_event_t        *ev;

    ngx_rbtree_node_t  *node, *root, *sentinel;

 

    sentinel = ngx_event_timer_rbtree.sentinel;

   

    //找到所有的超时的timer,然后处理它们

    for ( ;; ) {

 

        ngx_mutex_lock(ngx_event_timer_mutex);

 

        root = ngx_event_timer_rbtree.root;

 

        if (root == sentinel) {

            return;

        }

        //获取key最小的节点  

        node = ngx_rbtree_min(root, sentinel);

    

        //该节点是否超时

        if ((ngx_msec_int_t) (node->key - ngx_current_msec) <= 0) { 

            ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));

 

           //类似于ngx_event_del_timer

           ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);

 

            ngx_mutex_unlock(ngx_event_timer_mutex);

            ev->timer_set = 0;         

           ev->timedout = 1;

           ev->handler(ev);

 

            continue;

        }

 

        break;

    }

 

    ngx_mutex_unlock(ngx_event_timer_mutex);

}

5、一个例子

ngx_process_events_and_timers(ngx_cycle_t *cycle)

void

ngx_process_events_and_timers(ngx_cycle_t *cycle)

{

    ngx_uint_t  flags;

    ngx_msec_t  timer, delta;

 

    if (ngx_timer_resolution) {  //0

        timer = NGX_TIMER_INFINITE;

        flags = 0;

 

    } else {

        timer =ngx_event_find_timer();                        //返回的是最小的超时时间,单位是毫秒

        flags = NGX_UPDATE_TIME;// 1

 

#if (NGX_THREADS)

 

        if (timer == NGX_TIMER_INFINITE || timer > 500) {

            timer = 500;

        }

 

#endif

    }

 

    。。。

 

    delta = ngx_current_msec;

 

   //下面这个函数就是处理事件的函数(包括新连接建立事件),网络IO事件等等

    (void) ngx_process_events(cycle, timer, flags);   //ngx_epoll_process_events   

 

    delta = ngx_current_msec - delta;                       //时间差,因为ngx_time_update已更新

 

    。。。

 

    if (delta) {

        ngx_event_expire_timers();                           //处理所有的超时事件

    }

 

    。。。

}

Hello world

模块功能:注册一个定时事件,每过一秒钟打印一次hello worldngx_add_timer函数就是用来完成将一个新的定时事件加入定时器红黑树中,定时事件被执行后,就会从树中移除,因此要想不断的循环打印hello world,就需要在事件回调函数被调用后再将事件给添加到定时器红黑树中。 ngx_http_hello_process_init是注册在模块的进程初始化阶段的回调函数上。

static ngx_connection_t dummy;  

static ngx_event_t ev;   

 

 

static void  

ngx_http_hello_print(ngx_event_t *ev)   

{  

    printf("hello world\n");  

  

    ngx_add_timer(ev, 1000);  

}  

  

  

static ngx_int_t  

ngx_http_hello_process_init(ngx_cycle_t *cycle)  

{  

    dummy.fd = (ngx_socket_t) -1;   

  

    ngx_memzero(&ev, sizeof(ngx_event_t));  

  

    ev.handler = ngx_http_hello_print;  

    ev.log = cycle->log;  

    ev.data = &dummy;  

  

    ngx_add_timer(&ev, 1000);  

  

    return NGX_OK;  

}

6、参考资料

http://blog.dccmx.com/?p=537

http://blog.csdn.net/fjs_cloud/article/details/8722427

http://blog.csdn.net/marcky/article/details/7623335


原文地址:http://cjhust.blog.163.com/blog/static/175827157201348112639361

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值