最初的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便認爲已超時。可能會關閉鏈接,也可能會執行其餘操做。