走進nginx事件驅動模型
最近在看阿里陶輝前輩寫的”深入理解nginx”中的nginx的事件模組。之所以想看這塊內容,是因為nginx可以處理龐大的併發連線,想看看支援其背後的事件驅動是如何構建的
這篇博文我也不想貼程式碼什麼的整一堆東西來講述nginx事件驅動,一來我未必理解的那麼透徹,而來這樣反而更不易閱讀者快速的掌握知識,所以我會簡單的將幾個我認為可能會對我之後的伺服器程式開發中有所幫助的幾個知識點
簡單論述nginx的epoll模型
本文只以epoll為例
nginx以epoll為事件驅動的基礎,epoll共檢測4類事件,分別如下
.處理新連線事件 .處理定時事件 .處理普通讀寫事件 .處理從磁碟讀事件
(1)首先來談第一個處理新連線事件,我們在平時的伺服器設計時,由於連線事件比較敏感(對快速響應要求比較高),所以我會單開一個執行緒(程序)來專門處理連線,獲取連線後然後在分發給各個I/O複用執行緒,然而nginx的處理連線事件和處理其他事件都是在同一個I/O複用下,那麼它是如何保證連線事件對響應的要求的呢?niginx是通過將獲取的事件先不呼叫其回撥,而是把他們先放入倆個post佇列,這倆個佇列分別為
.ngx_posted_accept_events
.ngx_posted_events
第一個佇列用來儲存連線事件,而第二個佇列用來儲存普通讀寫事件,之後在執行時我們可以先保證ngx_posted_accept_events中的事件先處理,就可以保證連線對響應速度的敏感性
(2)如何防止串話
串話問題可以說是伺服器程式中都需要處理的一個問題。串話問題是指剛剛關閉了一個套接字,又來了一個新連線,而新連線剛好系統給分配的就是剛關閉的那個套接字,那麼如果方才哪個套接字還有事件未處理完成,接下來它給對應的套接字傳送資料很有可能就會發到新建立的使用者那。那麼nginx如何來解決這個問題呢?很簡單,nginx在每次獲得新連線後都會將連線中的一個標誌為置反,這樣本個連線和上個連線的instance就會不同,而每個事件都包含了連線,所以每次處理事件時只需要比較事件中的instance是否相同就OK了
(3)如何處理”驚群問題”
所謂驚群問題就是說多個程序在同時監聽同一個埠,當有連線到來時,系統會把多個程序都喚醒,但是當然任然只有一個程序能處理到新連線,所以本來其他程序是不需要被喚醒的,但是被喚醒了,這就是註明的驚群問題。nginx解決它的方法也很簡單,只需要保證同一時間點只有一個程序在監聽埠就可避免驚群問題了。但是問題的關鍵是如何能保證同一時間點只有一個程序來監聽埠。nginx採用了嘗試加鎖,根據加鎖的返回值確定本程序是否要接下來處理新連線事件,從而解決了”驚群問題”
(4)如何解決負載均衡
在我之前寫的一個小網路庫中,我所採用的負載均衡很簡單,就是主執行緒用來接受新連線,然後輪流把新連線分發給各個子執行緒,而nginx解決個程序間的負載均衡問題並沒有均衡分配,而是當每個程序處理的額連線數超過了規定其處理的最大連線數的7/8時,就會本次不處理連線,而是將其處理的連線數-1,這樣相當於就把機會讓給了其他執行緒,從而實現了負載均衡了
總結
關於nginx驅動模組就先寫這麼點吧,隨後在看的時候在不上,寫本片部落格只是用來督促自己學習的