我們先來介紹下nginx nginx : 支援高並發串連.官方測試的是5w並發串連但在實際生產中可製成2-4w並發串連數,得益於nginx使用最新的epoll(linux 2.6核心)和kqueue(freebsd)網路I/O模型.而apache使用的則是傳統的select模型,其比較穩定的prefork模式為多進程模式,需要經常派生子進程,所消耗的CPU等伺服器資源要比nginx高的多.select 和epoll效率差的原因: select是輪詢、epoll是觸發式的,所以效率高。單單這樣講,那能懂了才見鬼了.好...我們暫且客觀的記住這句話. 先說Select: 1.Socket數量限制:該模式可操作的Socket數由FD_SETSIZE決定,核心預設32*32=1024. 2.操作限制:通過遍曆FD_SETSIZE(1024)個Socket來完成調度,不管哪個Socket是活躍的,都遍曆一遍. 後說Poll: 1.Socket數量幾乎無限制:該模式下的Socket對應的fd列表由一個數組來儲存,大小不限(預設4k). 2.操作限制:同Select. 再說:Epoll: 1.Socket數量無限制:同Poll 2.操作無限制:基於核心提供的反射模式,有活躍Socket時,核心訪問該Socket的callback,不需要遍曆輪詢. 但是當所有Socket都活躍的時候,這時候所有的callback都被喚醒,會導致資源的競爭.既然都是要處理所有的Socket,那麼遍曆是最簡單最有效實現方式. 舉例來說: 對於IM伺服器,伺服器和伺服器之間都是長連結,但數量不多,一般一台60\70個,比如採用ICE這種架構設計,但請求相當頻繁和密集,這時候通過反射喚醒callback不一定比用select去遍曆處理更好. 對於web portal(門戶)伺服器,都是瀏覽器用戶端發起的http短連結請求,數量很大,好一點的網站動輒每分鐘上千個請求過來,同時伺服器端還有更多的閑置等待逾時的Socket,這時候沒必要把全部的Socket都遍曆處理,因為那些等待逾時的請求是大多數的,這樣用Epoll會更好.
支援一個進程開啟大數目的socket描述符 select 最不能忍受的是一個進程所開啟的FD是有一定限制的,由FD_SETSIZE設定,預設值是1024。對於那些需要支援的上萬串連數目的IM伺服器來說顯然太少了。這時候你一是可以選擇修改這個宏然後重新編譯核心,不過資料也同時指出這樣會帶來網路效率的下降,二是可以選擇多進程的解決方案(傳統的 Apache方案),不過雖然linux上面建立進程的代價比較小,但仍舊是不可忽視的,加上進程間資料同步遠 比不上線程間同步的高效,所以也不是一種完美的方案。不過 epoll則沒有這個限制,它所支援的FD上限是最大可以開啟檔案的數目,這個數字一般遠大於2048,舉個例子,在1GB記憶體的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統記憶體關係很大。 IO效率不隨FD數目增加而線性下降 傳統的select/poll另一個致命弱點就是當你擁有一個很大的socket集合,不過由於網路延時, 任一時間只有部分的socket是“活躍”的,但是select/poll每次調用都會線性掃描全部的集合,導致效率呈現線性下降。但是epoll不存在這個問題,它只會對“活躍”的socket進行操作---這是因為在核心實現中epoll是根據每個fd上面的callback函數實現的。那麼,只有“活躍”的socket才會主動的去調用 callback函數,其他idle狀態socket則不會,在這點上,epoll實現了一個“偽”AIO,因為這時候推動力在os核心。在一些 benchmark中,如果所有的socket基本上都是活躍的---比如一個高速LAN環境,epoll並不比select/poll有什麼效率,相反,如果過多使用epoll_ctl,效率相比還有稍微的下降。但是一旦使用idle connections類比WAN環境,epoll的效率就遠在select/poll之上了。 使用mmap加速核心與使用者空間的訊息傳遞。 這點實際上涉及到epoll的具體實現了。無論是select,poll還是epoll都需要核心把FD訊息通知給使用者空間,如何避免不必要的記憶體拷貝就很重要,在這點上,epoll是通過核心於使用者空間mmap同一塊記憶體實現的。而如果你想我一樣從2.5核心就關注epoll的話,一定不會忘記手工 mmap這一步的。 核心微調 這一點其實不算epoll的優點了,而是整個linux平台的優點。也許你可以懷疑linux平台,但是你無法迴避linux平台賦予你微調核心的能力。比如,核心TCP/IP協議棧 使用記憶體池管理sk_buff結構,那麼可以在運行時期動態調整這個記憶體pool(skb_head_pool)的大小--- 通過echo XXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函數的第2個參數(TCP完成3次握手 的資料包隊列長度),也可以根據你平台記憶體大小動態調整。更甚至在一個資料包面數目巨大但同時每個資料包本身大小卻很小的特殊系統上嘗試最新的NAPI網 卡驅動架構。 |