Nginx採用的是多進程模型,master進程創建好須要listen的socket fd以後,而後fork出N個worker進程。若是全部worker進程同時監聽這個fd的事件,當有新一個鏈接到來時,這些進程會被同時喚醒去處理事件——accept。可是隻有一個進程的accept會成功,而其餘進程都會失敗,這樣的現象叫作驚羣,會極大影響Nginx的性能。linux
其實,linux2.6內核已經解決了accept系統調用自己的驚羣問題了。若是在父進程中listen,而後fork出的全部子進程都accept這個fd。當新鏈接過來時,僅有一個子進程的accept會返回,其餘子進程則繼續阻塞在accept調用上。可是對於Nginx的epoll循環來講,這個問題依然存在,由於監聽的socket依然會同時產生可讀事件,把阻塞在epoll_wait上的全部worker所有喚醒。負載均衡
Nginx中處理epoll時驚羣問題的思路很簡單,引入了一個互斥鎖,worker進程誰拿到鎖,誰纔將accept的fd加入到epoll隊列中,其餘的子進程拿不到鎖,會將fd從epoll隊列中移除,新鏈接到來也就不會致使全部子進程的epoll_wait被喚醒返回。socket
在處理事件的函數ngx_process_events_and_timers中,worker會嘗試得到accept_mutex鎖,同一時刻只有一個worker能夠得到這把互斥鎖。若是一個worker拿到了鎖,就把監聽的fd加入epoll隊列,而且置NGX_POST_EVENTS標誌位,意思是會優先處理accept事件,把其餘全部事件放在accept後延後處理,由於在處理完accept事件後worker就能夠馬上釋放互斥鎖,這樣儘可能減小鎖的佔用時間。函數
在獲取accept_mutex鎖前還有一個負載均衡的邏輯,若是worker的當前活動鏈接數超過最大可承受鏈接數的7/8,則表示發生過載,不會去爭取鎖。性能