說明:本文章重點關注事件處理模型。有興趣的同學可以去http://tengine.taobao.org/book/查找更多資料。Tengine應該是淘寶基於Nginx自己做的修改。這個地址的文檔還在不斷的完善更新中,內容算是比較全面的。
程序流程圖:
說明:
一、進程生成順序
1.main(src/core/nginx.c)函數啟動ngx_master_process_cycle,啟動主服務進程。
2.ngx_master_process_cycle(src/os/unix/ngx_process_cycle.c)里面調用ngx_start_worker_processes.
3.ngx_start_worker_processes(src/os/unix/ngx_process_cycle.c)里面循環執行生成特定數量的進程池。
4.ngx_spawn_process(src/os/unix/ngx_process.c)根據指定的respawn選項fork子進程,相關子進程信息保存在全部ngx_processes數組(ngx_process_t ngx_processes[NGX_MAX_PROCESSES])中,子進程的運行過程通過參數proc指定,這里是ngx_worker_process_cycle(src/os/unix/ngx_process_cycle.c)。在fork之前先通過socketpair生成master process和worker process間進行通信的channel[2]。
master_process進程作為Nginx的服務主進程,管理其他子進程的生存周期,包括cache_manager_processes子進程,全部worker_processes,信號處理,timer等。
二、timer定時器超時處理機制
上圖中的左側紅色1,2,3步構成了timer和select/poll/epoll_wait等等待函數配合使用的基本流程,libevent里面的timer處理機制也是一樣的,即:
1.從timer樹(一般使用紅黑樹)中取出最小的timer;
2.傳入epoll_wait等I/O復用函數;
3.處理堆中的timer超時事件。
4.處理正常的連接事件。
流程圖:
補充:
由於需要對大量timer進行實時增刪和檢索,所以需要效率比較高的結構,紅黑樹是理想選擇。
概念上說管理timer的樹應該使用最小堆,每次只需要從樹根取出最小的timer。但是堆得問題是插入和刪除時都可能需要從根到葉節點的log(n)次交換,代價較大。
而紅黑樹的好處是插入和刪除時最多需要不超過3次旋轉操作,雖然總的復雜度都是O(logN),但基本都是顏色變換和key比較等簡單操作,不涉及節點交換(值交換)和旋轉(指針交換)。所以,從統計性能(可理解為cpu實際執行的指令數)來說,紅黑樹優於堆和AVL樹。
三、worker process I/O處理流程
每個worker process的運行過程ngx_worker_process_cycle如下:
1 static void 2 ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data) 3 { 4 ngx_int_t worker = (intptr_t) data; 5 6 ngx_process = NGX_PROCESS_WORKER; 7 ngx_worker = worker; 8 9 ngx_worker_process_init(cycle, worker); 10 11 ngx_setproctitle("worker process"); 12 13 for ( ;; ) { 14 15 if (ngx_exiting) { 16 ngx_event_cancel_timers(); 17 18 if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel) 19 { 20 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); 21 22 ngx_worker_process_exit(cycle); 23 } 24 } 25 26 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle"); 27 28 ngx_process_events_and_timers(cycle); 29 30 if (ngx_terminate) { 31 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); 32 33 ngx_worker_process_exit(cycle); 34 } 35 36 if (ngx_quit) { 37 ngx_quit = 0; 38 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, 39 "gracefully shutting down"); 40 ngx_setproctitle("worker process is shutting down"); 41 42 if (!ngx_exiting) { 43 ngx_exiting = 1; 44 ngx_close_listening_sockets(cycle); 45 ngx_close_idle_connections(cycle); 46 } 47 } 48 49 if (ngx_reopen) { 50 ngx_reopen = 0; 51 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs"); 52 ngx_reopen_files(cycle, -1); 53 } 54 } 55 }
可以看出,里面除了exiting,terminate,quit和reopen等控制操作外,只有ngx_process_events_and_timers(src/event/ngx_event.c)。再看ngx_process_events_and_timers函數:
1 void 2 ngx_process_events_and_timers(ngx_cycle_t *cycle) 3 { 4 ngx_uint_t flags; 5 ngx_msec_t timer, delta; 6 7 if (ngx_timer_resolution) { 8 timer = NGX_TIMER_INFINITE; 9 flags = 0; 10 11 } else { 12 timer = ngx_event_find_timer(); 13 flags = NGX_UPDATE_TIME; 14 15 #if (NGX_WIN32) 16 17 /* handle signals from master in case of network inactivity */ 18 19 if (timer == NGX_TIMER_INFINITE || timer > 500) { 20 timer = 500; 21 } 22 23 #endif 24 } 25 26 if (ngx_use_accept_mutex) { 27 if (ngx_accept_disabled > 0) { 28 ngx_accept_disabled--; 29 30 } else { 31 if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { 32 return; 33 } 34 35 if (ngx_accept_mutex_held) { 36 flags |= NGX_POST_EVENTS; 37 38 } else { 39 if (timer == NGX_TIMER_INFINITE 40 || timer > ngx_accept_mutex_delay) 41 { 42 timer = ngx_accept_mutex_delay; 43 } 44 } 45 } 46 } 47 48 delta = ngx_current_msec; 49 50 (void) ngx_process_events(cycle, timer, flags); 51 52 delta = ngx_current_msec - delta; 53 54 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, 55 "timer delta: %M", delta); 56 57 ngx_event_process_posted(cycle, &ngx_posted_accept_events); 58 59 if (ngx_accept_mutex_held) { 60 ngx_shmtx_unlock(&ngx_accept_mutex); 61 } 62 63 if (delta) { 64 ngx_event_expire_timers(); 65 } 66 67 ngx_event_process_posted(cycle, &ngx_posted_events); 68 }
1.函數開頭7-24行計算定時器timer,用於后面的ngx_process_events(50行)函數,最終用於各種I/O復用函數的timeout參數,如epoll_wait的timeout參數。ngx_process_events是一個宏,#define ngx_process_events ngx_event_actions.process_events(src/event/ngx_event.h)。全局變量ngx_event_actions在各個event module的init方法里面設置,如ngx_epoll_init(src/event/modules/ngx_epoll_module.c)中ngx_event_actions = ngx_epoll_module_ctx.actions;。而各個module的init方法的調用需要通過Nginx指定使用哪種類型的module來設置。相關初始設置在ngx_event_core_init_conf(src/event/ngx_event.c)里面指定。
2.63-65行處理timer超時事件。
以epoll module為例,ngx_process_events最終指向ngx_epoll_process_events(src/event/modules/ngx_epoll_module.c)。
1 static ngx_int_t 2 ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) 3 { 4 5 ...... 6 //將最近的timer作為epoll_wait的超時 7 events = epoll_wait(ep, event_list, (int) nevents, timer); 8 9 err = (events == -1) ? ngx_errno : 0; 10 11 ...... 12 //處理全部事件 13 for (i = 0; i < events; i++) { 14 15 ...... 16 17 if ((revents & EPOLLIN) && rev->active) { 18 19 rev->ready = 1; 20 21 if (flags & NGX_POST_EVENTS) { 22 queue = rev->accept ? &ngx_posted_accept_events 23 : &ngx_posted_events; 24 25 //將事件放入隊列,稍后處理 26 ngx_post_event(rev, queue); 27 28 } else { 29 rev->handler(rev); 30 } 31 } 32 33 wev = c->write; 34 35 if ((revents & EPOLLOUT) && wev->active) { 36 37 ...... 38 39 wev->ready = 1; 40 41 if (flags & NGX_POST_EVENTS) { 42 //將事件放入隊列,稍后處理 43 ngx_post_event(wev, &ngx_posted_events); 44 45 } else { 46 wev->handler(wev); 47 } 48 } 49 } 50 51 return NGX_OK; 52 }
可以看出,前面計算得到的timer傳進了epoll_wait里面。其實這個timer的值是通過函數 ngx_event_find_timer從全部的timer組成的最小堆(Nginx里面使用RB tree)里面取出來的最小timer值。 epoll_wait之后就是常見的事件處理了。將事件放入隊列主要是為快速釋放accept鎖,給其他worker進程機會去處理accept事件。
注:引用本人文章請注明出處,謝謝。