為什麼nginx效能比apache效能好
nginx用的相對於c++更底層的c編寫,有一定原因
兩種webserver的設計和定位的不同。
nginx自身定位為一個輕量級webserver,高階功能依賴於配置和載入模組組建。而apache自身功能強大,自身設計也是追求強大的穩定性。
同時最核心原因是二者網路IO處理的方式,nginx是非同步非阻塞,而apache是同步阻塞,這也是保障了nginx高效能和apache高穩定性的原因。
主要區別就是網路模型不同
apache->select
nginx->epoll (主要用這個)
簡介
select,epoll都是IO多路複用的機制。I/O多路複用就透過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。但select,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的
- I/O模式
1、阻塞 I/O:(Linux下的I/O操作預設是阻塞I/O)
2、非阻塞 I/O:(可以透過fcntl或者open時使用O_NONBLOCK引數,將fd設定為非阻塞的I/O)
3、I/O 多路複用:(I/O多路複用,通常需要非阻塞I/O配合使用)
4、訊號驅動 I/O
5、非同步 I/O
- select缺點:
1、每次呼叫select,都需要把fd集合從使用者態複製到核心態,這個開銷在fd很多時會很大
2、每次呼叫select都需要在核心遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大
3、select支援的檔案描述符數量太小了,預設是1024
- epoll
epoll是對select的改進,可以避免上述的三個缺點,我們先看一下epoll和select的呼叫介面上的不同。
select只提供了一個函式select,epoll提供了三個函式
epoll_create:建立一個epoll控制程式碼
epoll_ctl:註冊要監聽的事件型別
epoll_wait:等待事件的產生
對於1:epoll的解決方案在epoll_ctl函式中。每次註冊新的事件到epoll控制程式碼中時(在epoll_ctl中指定EPOLL_CTL_ADD),會把所有的fd複製進核心,而不是在epoll_wait的時候重複複製,epoll保證了每個fd在整個過程中只會複製一次。
對於2:epoll的解決方案不像select那樣每次都把current輪流加入fd對應的裝置等待佇列中,而只在epoll_ctl時把current掛一遍(這一遍必不可少)併為每個fd指定一個回撥函式,當裝置就緒,喚醒等待佇列上的等待者時,就會呼叫這個回撥函式,而這個回撥函式會把就緒的fd加入一個就緒連結串列)。epoll_wait的工作實際上就是在這個就緒連結串列中檢視有沒有就緒的fd(利用schedule_timeout()實現睡一會,判斷一會的效果,和select實現是類似的)。
對於3:epoll沒有這個限制,它所支援的FD上限是最大可以開啟檔案的數目,這個數字一般遠大於2048,舉個例子,在1GB記憶體的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統記憶體關係很大。
- epoll的優點
1、監視的描述符數量不受限制,select的最大缺點就是程式開啟的fd是有數量限制的。這對於連線數量比較大的伺服器來說根本不能滿足。雖然也可以選擇多程式的解決方案,不過雖然linux上面建立程式的代價比較小,但仍舊是不可忽視的,加上程式間資料同步遠比不上執行緒間同步的高效,所以也不是一種完美的方案。
2、IO的效率不會隨著監視fd的數量的增長而下降。epoll不同於select輪詢的方式,而是透過每個fd定義的回撥函式來實現的,只有就緒的fd才會執行回撥函式。
3、支援電平觸發和邊沿觸發(只告訴程式哪些檔案描述符剛剛變為就緒狀態,它只說一遍,如果我們沒有采取行動,那麼它將不會再次告知,這種方式稱為邊緣觸發)兩種方式,理論上邊緣觸發的效能要更高一些,但是程式碼實現相當複雜。
總結:
1. select實現需要自己不斷輪詢所有fd集合,直到裝置就緒,期間可能要睡眠和喚醒多次交替。而epoll其實也需要呼叫epoll_wait不斷輪詢就緒連結串列,期間也可能多次睡眠和喚醒交替,但是它是裝置就緒時,呼叫回撥函式,把就緒fd放入就緒連結串列中,並喚醒在epoll_wait中進入睡眠的程式。雖然都要睡眠和交替,但是select和poll在“醒著”的時候要遍歷整個fd集合,而epoll在“醒著”的時候只要判斷一下就緒連結串列是否為空就行了,這節省了大量的CPU時間。這就是回撥機制帶來的效能提升。
2. select每次呼叫都要把fd集合從使用者態往核心態複製一次,並且要把current往裝置等待佇列中掛一次,而epoll只要一次複製,而且把current往等待佇列上掛也只掛一次(在epoll_wait的開始,注意這裡的等待佇列並不是裝置等待佇列,只是一個epoll內部定義的等待佇列)。這也能節省不少的開銷。
3. 在 select中,程式只有在呼叫一定的方法後,核心才對所有監視的檔案描述符進行掃描,而epoll事先透過epoll_ctl()來註冊一 個檔案描述符,一旦基於某個檔案描述符就緒時,核心會採用類似callback的回撥機制,迅速啟用這個檔案描述符,當程式呼叫epoll_wait() 時便得到通知。
4. mmap加速核心與使用者空間的資訊傳遞。epoll是透過核心於使用者空間mmap同一塊記憶體,避免了無畏的記憶體複製。
所以嗎,效率就看出來了,不過nginx在穩定性上比apache要差
本作品採用《CC 協議》,轉載必須註明作者和本文連結