*什麼是驚羣現象?Nginx中用了什麼方法來避免這種問題的發生?本篇就解決這兩個問題。。。→_→*
驚羣現象的定義與危害
在Nginx中,每一個worker進程都是由master進程fork出來的。master進程創建socket後進行listen、bind操作,fork出來的worker繼承了socket,調用accpet開始監聽等待網絡連接
如果這時有多個worker進程都在等待事件的發生。當事件發生時,這些worker進程被同時喚醒,但最終只有一個worker進程可以處理事件成功,其他的worker進程就會重新進入阻塞狀態
當驚羣現象發生時,內核會依次喚醒所有的worker進程,這種操作會導致系統在瞬時佔用極大的資源,但最後卻只有一個worker進程處理事件成功,這就造成了極大的資源浪費
Nginx中解決驚羣現象的方法
- Nginx中規定同一時刻只能有唯一一個的worker進程監聽Web端口,這樣就不會發生驚羣了,此時新連接事件只能喚醒唯一正在監聽端口的worker進程
源碼剖析
ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
//使用worker進程間同步鎖——ngx_accept_mutex,ngx_shmtx_trylock返回1表示成功獲取鎖,返回0表示獲取鎖失敗。ngx_shmtx_trylock是非阻塞的,如果此時ngx_accept_mutex被其他worker進程佔有,那麼ngx_shmtx_trylock會立即返回
if (ngx_shmtx_trylock(&ngx_accept_mutex)) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"accept mutex locked");
//ngx_accept_mutex_held爲1時表示當前worker進程已經獲取到了鎖,那麼就立即返回
if (ngx_accept_mutex_held
&& ngx_accept_events == 0
&& !(ngx_event_flags & NGX_USE_RTSIG_EVENT))
{
return NGX_OK;
}
//將所有監聽連接的讀事件添加到當前的epoll等事件驅動模塊中
if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
//如果將所有監聽連接的讀事件添加到當前的epoll等事件驅動模塊中失敗,那麼就必須釋放ngx_accept_mutex鎖
ngx_shmtx_unlock(&ngx_accept_mutex);
return NGX_ERROR;
}
//此時需要把ngx_accept_mutex_held置爲1,方便本進程的其他驅動模塊它已經獲取到了鎖
ngx_accept_events = 0;
ngx_accept_mutex_held = 1;
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"accept mutex lock failed: %ui", ngx_accept_mutex_held);
//此時ngx_shmtx_trylock返回了0,表示獲取ngx_shmtx_trylock鎖失敗。但是此時ngx_accept_mutex_held還爲1,即當前worker進程還在佔有ngx_accept_mutex鎖,就說明有問題
if (ngx_accept_mutex_held) {
//將所有監聽連接的讀事件從事件模塊中移出
if (ngx_disable_accept_events(cycle) == NGX_ERROR) {
return NGX_ERROR;
}
//沒有獲取到ngx_accept_mutex鎖時,將ngx_accept_mutex_held置爲0
ngx_accept_mutex_held = 0;
}
return NGX_OK;
}
*本篇只分析了Nginx中如何保證不發生驚羣現象的解決方法,後面其實還有worker進程何時釋放ngx_accept_mutex鎖的問題。。其超出了本篇的範圍。。。就不在這裏繼續討論了。。明天加油。。。→_→*