當(dāng)客戶端發(fā)起連接后路星,由于所有的worker子進(jìn)程都監(jiān)聽著同一個端口,內(nèi)核協(xié)議棧在檢測到客戶端連接后,會激活所有休眠的worker子進(jìn)程克懊,最終只會有一個子進(jìn)程成功建立新連接,其他子進(jìn)程都會accept失敗七蜘。
Accept失敗的子進(jìn)程是不應(yīng)該被內(nèi)核喚醒的谭溉,因為它們被喚醒的操作是多余的,占用本不應(yīng)該被占用的系統(tǒng)資源橡卤,引起不必要的進(jìn)程上下文切換扮念,增加了系統(tǒng)開銷,同時也影響了客戶端連接的時延碧库。
“驚群”問題是多個子進(jìn)程同時監(jiān)聽同一個端口引起的柜与,因此解決的方法是同一時刻只讓一個子進(jìn)程監(jiān)聽服務(wù)器端口巧勤,這樣新連接事件只會喚醒唯一正在監(jiān)聽端口的子進(jìn)程。
因此“驚群”問題通過非阻塞的accept鎖來實現(xiàn)進(jìn)程互斥accept()弄匕,其原理是:在worker進(jìn)程主循環(huán)中非阻塞trylock獲取accept鎖颅悉,如果trylock成功,則此進(jìn)程把監(jiān)聽端口對應(yīng)的fd通過epoll_ctl()加入到本進(jìn)程自由的epoll事件集迁匠;如果trylock失敗剩瓶,則把監(jiān)聽fd從本進(jìn)程對應(yīng)的epoll事件集中清除。
Nginx實現(xiàn)了兩套互斥鎖:基于原子操作和信號量實現(xiàn)的互斥鎖城丧、基于文件鎖封裝的互斥鎖延曙。考慮到鎖的平臺可移植性和通用性芙贫,改造twemproxy選擇時搂鲫,選擇文件鎖實現(xiàn)。
如果獲取accept鎖成功的進(jìn)程占用鎖時間過長磺平,那么其他空閑進(jìn)程在這段時間內(nèi)無法獲取到鎖魂仍,從而無法接受新的連接。最終造成客戶端連接相應(yīng)時間變長拣挪,qps低擦酌,同時引起負(fù)載嚴(yán)重不均衡。為了解決該問題菠劝,選擇通過post事件隊列方式來提高性能赊舶,trylock獲取到accept鎖成功的進(jìn)程,其工作流程如下:
trylock獲取accept鎖成功
通過epoll_wait獲取所有的事件信息赶诊,把監(jiān)聽到的所有accept事件信息加入accept_post列表笼平,把已有連接觸發(fā)的讀寫事件信息加入read_write_post列表。
執(zhí)行accept_post列表中的所有事件
Unlock鎖
執(zhí)行read_write_post列表中的事件舔痪。
Worker進(jìn)程主循環(huán)工作流程圖如下:
從上圖可以看出寓调,worker進(jìn)程借助epoll來實現(xiàn)網(wǎng)絡(luò)異步收發(fā),客戶端連接twemproxy的時候锄码,worker進(jìn)程循環(huán)檢測客戶端的各種網(wǎng)絡(luò)事件和后端memcached的網(wǎng)絡(luò)事件夺英,并進(jìn)行相應(yīng)的處理。