![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyQjN4AzNyIDMy0SOzkjNwEjM4ETOwgDMwIDMy0SN4QTOxQTMvwFOwAjMwIzLcVDO0kTM0EzLcd2bsJ2Lc12bj5ycn9Gbi52YuAjMwIzZtl2Lc9CX6MHc0RHaiojIsJye.png)
static void
ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data)
{-----------------------------------
/*
* Set correct process type since closing listening Unix domain socket
* in a master process also removes the Unix domain socket file.
*/
ngx_process = NGX_PROCESS_HELPER;
ngx_close_listening_sockets(cycle);
/* Set a moderate number of connections for a helper process. */
cycle->connection_n = 512;
//work 程序 初始化
ngx_worker_process_init(cycle, -1);
ngx_memzero(&ev, sizeof(ngx_event_t));
ev.handler = ctx->handler; //ngx_cache_manager_process_handler ngx_cache_loader_process_handler
ev.data = ident;
ev.log = cycle->log;
ident[3] = (void *) -1;
ngx_use_accept_mutex = 0;
ngx_setproctitle(ctx->name);
ngx_add_timer(&ev, ctx->delay, NGX_FUNC_LINE); //ctx->dealy秒執行ctx->handler;
for ( ;; ) {
if (ngx_terminate || ngx_quit) {
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
exit(0);
}
if (ngx_reopen) {
ngx_reopen = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
ngx_reopen_files(cycle, -1);
}
------------//work 程序 循環處理 fd io 以及 timer------------------------------------------------------
ngx_process_events_and_timers(cycle);
}
}
void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
ngx_uint_t flags;
ngx_msec_t timer, delta;
---------------------------------------
ngx_use_accept_mutex = 1;
//ngx_use_accept_mutex表示是否需要通過對accept加鎖來解決驚群問題。當nginx worker程序數>1時且配置檔案中打開accept_mutex時,這個标志置為1
if (ngx_use_accept_mutex) {
/*
ngx_accept_disabled表示此時滿負荷,沒必要再處理新連接配接了,我們在nginx.conf曾經配置了每一個nginx worker程序能夠處理的最大連接配接數,
當達到最大數的7/8時,ngx_accept_disabled為正,說明本nginx worker程序非常繁忙,将不再去處理新連接配接,這也是個簡單的負載均衡
在目前使用的連接配接到達總連接配接數的7/8時,就不會再處理新連接配接了,同時,在每次調用process_events時都會将ngx_accept_disabled減1,
直到ngx_accept_disabled降到總連接配接數的7/8以下時,才會調用ngx_trylock_accept_mutex試圖去處理新連接配接事件 如果ngx_accept_disabled大于了0,就表示該程序接受的
連接配接過多,是以就放棄一次争搶accept mutex的機會,同時将 自己減1。然後,繼續處理已有連接配接上的事件。Nginx就借用 此變量實作了程序關于連接配接的基本負載均衡。
*/
if (ngx_accept_disabled > 0) { //為正說明可用連接配接用了超過八分之七,則讓其他的程序在下面的else中來accept
ngx_accept_disabled--;
} else {
/*
如果ngx_trylock_accept_mutex方法沒有擷取到鎖,接下來調用事件驅動子產品的process_events方法時隻能處理已有的連接配接上的事件;
如果擷取到了鎖,調用process_events方法時就會既處理已有連接配接上的事件,也處理新連接配接的事件。
如何用鎖來避免驚群?
嘗試鎖accept mutex,隻有成功擷取鎖的程序,才會将listen
套接字放入epoll中。是以,這就保證了隻有一個程序擁有
監聽套接口,故所有程序阻塞在epoll_wait時,不會出現驚群現象。
這裡的ngx_trylock_accept_mutex函數中,如果順利的擷取了鎖,那麼它會将監聽端口注冊到目前worker程序的epoll當中
獲得accept鎖,多個worker僅有一個可以得到這把鎖。獲得鎖不是阻塞過程,都是立刻傳回,擷取成功的話ngx_accept_mutex_held被置為1。
拿到鎖,意味着監聽句柄被放到本程序的epoll中了,如果沒有拿到鎖,則監聽句柄會被從epoll中取出。
*/
/*
如果ngx_use_accept_mutex為0也就是未開啟accept_mutex鎖,則在ngx_worker_process_init->ngx_event_process_init 中把accept連接配接讀事件統計到epoll中
否則在ngx_process_events_and_timers->ngx_process_events_and_timers->ngx_trylock_accept_mutex中把accept連接配接讀事件統計到epoll中;
---------------------------嘗試鎖accept mutex,隻有成功擷取鎖的程序,才會将listen 套接字放入epoll中。是以,這就保證了隻有一個程序擁有 監聽套接口,故所有程序阻塞在epoll_wait時,不會出現驚群現象。
*/
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { //不管是擷取到鎖還是沒擷取到鎖都是傳回NGX_OK
return;
}
/*
拿到鎖的話,置flag為NGX_POST_EVENTS,這意味着ngx_process_events函數中,任何事件都将延後處理,會把accept事件都放到
ngx_posted_accept_events連結清單中,epollin|epollout事件都放到ngx_posted_events連結清單中 ---
擷取鎖的程序,将添加一個NGX_POST_EVENTS标志, 此标志的作用是将所有産生的事件放入一個隊列中, 等釋放鎖後,再慢慢來處理事件。
因為,處理事件可能 會很耗時,如果不先釋放鎖再處理的話,該程序就長 時間霸占了鎖,導緻其他程序無法擷取鎖,這樣accept 的效率就低了。
*/
if (ngx_accept_mutex_held) {
flags |= NGX_POST_EVENTS;
} else {
/*
拿不到鎖,也就不會處理監聽的句柄,這個timer實際是傳給epoll_wait的逾時時間,修改為最大ngx_accept_mutex_delay意味
着epoll_wait更短的逾時傳回,以免新連接配接長時間沒有得到處理
*/
if (timer == NGX_TIMER_INFINITE
|| timer > ngx_accept_mutex_delay)
{ //如果沒擷取到鎖,則延遲這麼多ms重新擷取說,繼續循環,也就是技術鎖被其他程序獲得,本程序最多在epoll_wait中睡眠0.5s,然後傳回
timer = ngx_accept_mutex_delay; //保證這麼多時間逾時的時候出發epoll_wait傳回,進而可以更新記憶體時間
}
}
}
}
delta = ngx_current_msec;
/*
1.如果程序獲的鎖,并擷取到鎖,則該程序在epoll事件發生後會觸發傳回,然後得到對應的事件handler,加入延遲隊列中,然後釋放鎖,然
後在執行對應handler,同時更新時間,判斷該程序對應的紅黑樹中是否有定時器逾時,
2.如果沒有擷取到鎖,則預設傳給epoll_wait的逾時時間是0.5s,表示過0.5s繼續擷取鎖,0.5s逾時後,會跟新目前時間,同時判斷是否有過期的
定時器,有則指向對應的定時器函數
*/
/*
1.ngx_event_s可以是普通的epoll讀寫事件(參考ngx_event_connect_peer->ngx_add_conn或者ngx_add_event),通過讀寫事件觸發
2.也可以是普通定時器事件(參考ngx_cache_manager_process_handler->ngx_add_timer(ngx_event_add_timer)),通過ngx_process_events_and_timers中的
epoll_wait傳回,可以是讀寫事件觸發傳回,也可能是因為沒擷取到共享鎖,進而等待0.5s傳回重新擷取鎖來跟新事件并執行逾時事件來跟新事件并且判斷定
時器連結清單中的逾時事件,逾時則執行進而指向event的handler,然後進一步指向對應r或者u的->write_event_handler read_event_handler
3.也可以是利用定時器expirt實作的讀寫事件(參考ngx_http_set_write_handler->ngx_add_timer(ngx_event_add_timer)),觸發過程見2,隻是在handler中不會執行write_event_handler read_event_handler
*/
//linux下,普通網絡套接字調用ngx_epoll_process_events函數開始處理,異步檔案i/o設定事件的回調方法為ngx_epoll_eventfd_handler
(void) ngx_process_events(cycle, timer, flags);
delta = ngx_current_msec - delta; //(void) ngx_process_events(cycle, timer, flags)中epoll等待事件觸發過程花費的時間
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "epoll_wait timer range(delta): %M", delta);
//當感應到來自于用戶端的accept事件,epoll_wait傳回後加入到post隊列,執行完所有accpet連接配接事件後,立馬釋放ngx_accept_mutex鎖,這樣其他程序就可以立馬獲得鎖accept用戶端連接配接
//* 這裡完成對隊列中的accept事件的處理,實際就是調用 ngx_event_accept函數來擷取一個新的連接配接,然後放入 epoll中。
ngx_event_process_posted(cycle, &ngx_posted_accept_events); //一般執行ngx_event_accept
//釋放鎖後再處理下面的EPOLLIN EPOLLOUT請求 --------------所有accept事件處理完成,如果擁有鎖的話,就趕緊釋放了。 其他程序還等着搶了。
if (ngx_accept_mutex_held) {
ngx_shmtx_unlock(&ngx_accept_mutex);
}
if (delta) {
ngx_event_expire_timers(); //處理紅黑樹隊列中的逾時事件handler
}
/*
然後再處理正常的資料讀寫請求。因為這些請求耗時久,是以在ngx_process_events裡NGX_POST_EVENTS标志将事件都放入ngx_posted_events
連結清單中,延遲到鎖釋放了再處理。
*/
ngx_event_process_posted(cycle, &ngx_posted_events); //普通讀寫事件放在釋放ngx_accept_mutex鎖後執行,提高用戶端accept性能
}
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyQjN4AzNyIDMy0SOzkjNwEjM4ETOwgDMwIDMy0SN4QTOxQTMvwFOwAjMwIzLcVDO0kTM0EzLcd2bsJ2Lc12bj5ycn9Gbi52YuAjMwIzZtl2Lc9CX6MHc0RHaiojIsJye.png)
https://yikun.github.io/2014/03/26/nginxevent/
http代理伺服器(3-4-7層代理)-網絡事件庫公共元件、核心kernel驅動 攝像頭驅動 tcpip網絡協定棧、netfilter、bridge 好像看過!!!!
但行好事 莫問前程
--身高體重180的胖子