最初的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便认为已超时。可能会关闭链接,也可能会执行其余操做。