epoll_wait驚群問題
項目接入層用的模型是,主線程創建listenfd,傳入6個子線程,每個子線程一個事件循環,epoll_wait這個listenfd。
如果是listenfd,則epoll_wait返回調用accept,其它fd則另外處理。
這裏有個epoll_wait的驚群現象:
當一個新連接到達(connect),所有等待在此listenfd上的線程均會被喚醒,進入到事件處理循環中去,但只有一個循環中的accept()會返回,其它線程均返回-1,並置EAGAIN (11)的errno;
Linux在內核層解決了accept的驚群問題,即accept在同一listenfd上的多個線程,事件到達時只有一個會被喚醒。
但Linux為什麽不在內核解決epoll_wait的驚群問題呢?
因為對於listenfd,有確切的語義,只會調用accept。但epoll_wait可以等待listenfd和socketfd及其它多種事件,像socketfd可能存在由多個線程同時去讀的情景(只是舉例,一般網絡編程也無這種場景應用);Linux內核無法加以區分。
————
如何解決epoll_wait的驚群問題?
參考Nginx的方案,使用一個accept_mutex鎖,同一時刻listenfd只會加入到其中一個線程的事件循環中監聽;當前進程處理的連接達到一定規模後,釋放掉mutex,即不再處理新的連接請求。這樣其它低負載的線程會拿到鎖,去監聽;這樣也達到一個負載均衡的效果;
————
驚群帶來的效率損失,主要在頻繁的喚起epoll_wait,帶來的系統調用開銷。我們目前的處理方式為:
大量TCP長連接,不存在驚群引入的效率問題,因此未處理;只在accept返回-1,errno == 11時,作continue處理;
epoll_wait驚群問題