Nginx事件驱动框架

最初的Web服务器如Apache,采用的是fork and run的模式,对每一个到来的链接,fork一个进程去处理,处理完成后进程退出。优势是编程实现简单,缺点是并发处理能力不足。为应对高并发的处理,以Nginx为表明的异步处理方式应运而生。node

Nginx以事件驱动工做,事件的来源有两个,网络IO和定时器。linux

对于高并发的网络IO处理,不一样的操做系统提供了不一样的解决方案,如linux的epoll, freebsd的kqueue。这里以epoll为例。将须要监听的socket加入到epoll中后,即可以经过epoll_wait获取已发生的事件,避免对众多的socket进行轮寻。编程

Nginx的定时器采用红黑数实现,每一个定时器事件以超时时间为key插入到红黑树中,每次取红黑树中key最小的结点与当前的系统时间比较便可知道是否超时。服务器

Nginx工做进程处理任务的核心在ngx_process_events_and_timers函数中。网络

for ( ;; ) {

        ...................

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");

        ngx_process_events_and_timers(cycle);

        ...................
    }

ngx_process_events_and_timers主要逻辑以下所示。并发

void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{


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

    ngx_event_process_posted(cycle, &ngx_posted_accept_events);

    
    ngx_event_expire_timers();

    ngx_event_process_posted(cycle, &ngx_posted_events);
}

函数中先调用ngx_process_events处理epoll_wait获得的网络IO事件,再调用ngx_event_expire_timers处理全部的超时事件。异步

static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
    int                events;
    uint32_t           revents;
    ngx_int_t          instance, i;
    ngx_uint_t         level;
    ngx_err_t          err;
    ngx_event_t       *rev, *wev;
    ngx_queue_t       *queue;
    ngx_connection_t  *c;


    events = epoll_wait(ep, event_list, (int) nevents, timer);

    for (i = 0; i < events; i++) {
        c = event_list[i].data.ptr;
        rev = c->read;

        revents = event_list[i].events;


        if ((revents & EPOLLIN) && rev->active) {
             rev->handler(rev);
        }

        wev = c->write;

        if ((revents & EPOLLOUT) && wev->active) {

             wev->handler(wev);
        }
    }

    return NGX_OK;
}

利用epoll模式时ngx_process_events即为ngx_epoll_process_events,上面的代码中省略了无关的部分,当revents & EPOLLIN为true时socket有数据能够读取,revents & EPOLLOUT为true时socket可写,而后调用相应的handler回调函数进行处理。socket

ngx_event_expire_timers处理定时器事件逻辑更简单一些,主要就是调用ngx_rbtree_min获取超时时间最近的事件,若是已超时即调用ev->handler进行处理。函数

void
ngx_event_expire_timers(void)
{
    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->timer_set = 0;

        ev->timedout = 1;

        ev->handler(ev);
    }
}

 

对于网络IO主要操做就是读和写,现实网络环境很是复杂,链接会由于各类缘由中断,没法传输数据。同时服务器端的每一个网络链接都要消耗服务器资源,为了不无效的链接一直占用系用的资源,须要对读写操做设置超时机制,为此Nginx作了专门的处理。高并发

对每一个到来的连接,Nginx分配一个ngx_connection_t结构体存储相应的信息,每一个ngx_connection_t结构体都有两个成员read和write,对应两个ngx_event_t事件。当须要从socket读取数据时,将socket加入到epoll监听事件中,同时将对应的read事件加入到定时器中,若是定时器超时后仍然没有数据可读,便认为读取数据超时。

事件超时时,Nginx会将ev->timedout置为1,再调用ev->handler。相应的Nginx中对每一个事件的handler回调函数大多会有以下的逻辑

if (ev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        ngx_http_close_connection(c);
        return;
    }

handler回调函数的开头判断事件是否已超时,若是ev->timedout不为0便认为已超时。可能会关闭链接,也可能会执行其余操做。

相关文章
相关标签/搜索