Linux Apache和Nginx網路模型詳解

Dus發表於2015-02-20

程式阻塞和掛起的定義:

    阻塞是由於程式所需資源得不到滿足,並會最終導致程式被掛起

    程式掛起的原因並不一定是由於阻塞,也有可能是時間片得不到滿足,掛起狀態是程式從記憶體排程到外存中的一種狀態,若在就緒態時,從記憶體調出到外存中,就是就緒掛起態,若在阻塞態時,從記憶體調出到外存中,就轉換成了阻塞掛起態
 
 

 Nginx的高併發得益於其採用了epoll模型,與傳統的伺服器程式架構不同,epoll是linux核心2.6以後才出現的。下面通過比較Apache和Nginx工作原理來比較。

 

      傳統Apache都是多程式或者多執行緒來工作,假設是多程式工作(prefork),apache會先生成幾個程式,類似程式池的工作原理,只不過這裡的程式池會隨著請求數目的增加而增加。對於每一個連線,apache都是在一個程式內處理完畢。具體是 recv(),以及根據 URI 去進行磁碟I/O來尋找檔案,還有 send()都是阻塞的。其實說白了都是 apche 對於套接字的I/O,讀或者寫,但是讀或者寫都是阻塞的,阻塞意味著程式就得掛起進入sleep狀態,那麼一旦連線數很多,Apache必然要生成更多的程式來響應請求,一旦程式多了,CPU對於程式的切換就頻繁了,很耗資源和時間,所以就導致apache效能下降了,說白了就是處理不過來這麼多程式了。其實仔細想想,如果對於程式每個請求都沒有阻塞,那麼效率肯定會提高很多。

 

      Nginx採用epoll模型,非同步非阻塞。對於Nginx來說,把一個完整的連線請求處理都劃分成了事件,一個一個的事件。比如accept(), recv(),磁碟I/O,send()等,每部分都有相應的模組去處理,一個完整的請求可能是由幾百個模組去處理。真正核心的就是事件收集和分發模組,這就是管理所有模組的核心。只有核心模組的排程才能讓對應的模組佔用CPU資源,從而處理請求。拿一個HTTP請求來說,首先在事件收集分發模組註冊感興趣的監聽事件,註冊好之後不阻塞直接返回,接下來就不需要再管了,等待有連線來了核心會通知你(epoll的輪詢會告訴程式),cpu就可以處理其他事情去了。一旦有請求來,那麼對整個請求分配相應的上下文(其實已經預先分配好),這時候再註冊新的感興趣的事件(read函式),同樣客戶端資料來了核心會自動通知程式可以去讀資料了,讀了資料之後就是解析,解析完後去磁碟找資源(I/O),一旦I/O完成會通知程式,程式開始給客戶端發回資料send(),這時候也不是阻塞的,呼叫後就等核心發回通知傳送的結果就行。整個下來把一個請求分成了很多個階段,每個階段都到很多模組去註冊,然後處理,都是非同步非阻塞。非同步這裡指的就是做一個事情,不需要等返回結果,做好了會自動通知你。

select/epoll的特點

select的特點:select 選擇控制程式碼的時候,是遍歷所有控制程式碼,也就是說控制程式碼有事件響應時,select需要遍歷所有控制程式碼才能獲取到哪些控制程式碼有事件通知,因此效率是非常低。但是如果連線很少的情況下, select和epoll的LT觸發模式相比, 效能上差別不大。
這裡要多說一句,select支援的控制程式碼數是有限制的, 同時只支援1024個,這個是控制程式碼集合限制的,如果超過這個限制,很可能導致溢位,而且非常不容易發現問題, 當然可以通過修改linux的socket核心調整這個引數。
epoll的特點:epoll對於控制程式碼事件的選擇不是遍歷的,是事件響應的,就是控制程式碼上事件來就馬上選擇出來,不需要遍歷整個控制程式碼連結串列,因此效率非常高,核心將控制程式碼用紅黑樹儲存的。
對於epoll而言還有ET和LT的區別,LT表示水平觸發,ET表示邊緣觸發,兩者在效能以及程式碼實現上差別也是非常大的。

 
 

相關文章