兩種IO模型,都屬於多路IO就緒通知,提供了對大量文件描述符就緒檢查的高性能方案,只不過實現方式有所不一樣:web
select:apache
一個select()系統調用來監視包含多個文件描述符的數組,當select返回,該數組中就緒的文件描述符便會被內核修改標誌位。數組
select的 跨平臺 作的很好,幾乎每一個平臺都支持。服務器
select缺點有如下三點:網絡
單個進程可以 監視的文件描述符的數量存在最大限制數據結構
select()所維護的 存儲大量文件描述符的數據結構 ,隨着文件描述符數量的增加,其在用戶態和內核的地址空間的複製所引起的開銷也會線性增加併發
因爲網絡響應時間的延遲使得大量TCP鏈接處於非活躍狀態,但調用select()仍是會對 全部的socket進行一次線性掃描 ,會形成必定的開銷socket
poll:tcp
poll是unix沿用select本身從新實現了一遍,惟一解決的問題是poll 沒有最大文件描述符數量的限制ide
epoll:
epoll帶來了兩個優點,大幅度提高了性能:
基於事件的就緒通知方式 ,select/poll方式,進程只有在調用必定的方法後,內核纔會對全部監視的文件描述符進行掃描,而epoll事件經過epoll_ctl()註冊一個文件描述符,一旦某個文件描述符就緒時,內核會採用相似call back的回調機制,迅速激活這個文件描述符,epoll_wait()便會獲得通知
調用一次epoll_wait()得到就緒文件描述符時,返回的並非實際的描述符,而是一個表明就緒描述符數量的值,拿到這些值去epoll指定的一個數組中依次取得相應數量的文件描述符便可,這裏使用內存映射(mmap)技術, 避免了複製大量文件描述符帶來的開銷
固然epoll也有必定的侷限性, epoll只有Linux2.6纔有實現 ,而其餘平臺都沒有,這和apache這種優秀的跨平臺服務器,顯然是有些背道而馳了。
Apache與Nginx的性能誰更高效,取決於其服務器的併發策略以及其面對的場景:
併發策略:
咱們目前使用的 Apache是基於一個線程處理一個請求的非阻塞IO併發策略 。這種方式容許一個進程中經過多個線程來處理多個鏈接,其中每一個線程處理一個鏈接。Apache使用其worker模塊實現這種方式,目的是減小perfork模式中太多進程的開銷,使得apache能夠支持更多的併發鏈接。
至於,非阻塞IO的實現,是經過一個子進程負責accept(),一旦接收到鏈接後,便將任務分配給適當worker的線程。
因爲apache的線程使用的是內核進程調度器管理的輕量級進程,所以與perfork模式比較,進程上下文切換的開銷依然存在,性能提高不是很明顯。
而 Nginx使用的是一個進程處理多個鏈接、非阻塞IO模式 ,這種模式最特別的是設計了獨立的listener進程,專門負責接收新的鏈接,再分配給各個worker,固然爲了減小任務調度的開銷,通常都是由worker進程來進行接收。
而IO模型層面,Nginx選擇epoll,此方式高效主要在於其基於事件的就緒通知機制,在高鏈接數的場景下,epoll通知方式更具優點。另外,epoll方式只關注活躍鏈接,而不像select方式須要掃描全部的文件描述符,這樣在大量鏈接的場景下,epoll方式優點會更加明顯。
面對的場景:
咱們反觀一下網站目前的情況和場景:
上圖能夠看到,rt監控當中,因爲咱們是動態網站,網站的大部份內容都須要動態獲取、計算、輸出,所以響應時間廣泛位於100毫秒以上,響應時間較長,服務器必將建立更多的鏈接處理這些請求,而tcp監控當中,能夠看到因爲TCP協議特有的特性,服務端主動關閉一個鏈接,鏈接會進入等待超時的狀態,且此狀態會持續2MSL(即兩倍的數據包最大生存時間,這個時 間長短跟操做系統有關,通常會在1-4分鐘),所以服務器端會保留必定量time_wait鏈接,管理大量的鏈接也會對服務器形成必定的成本,而epoll在多鏈接併發處理以及管理這兩方面,都較於select具備很大的優點。這也正是高併發、高鏈接的互聯網網站大量使用Nginx服務器的緣由所在。