select與epoll、apache與nginx實現原理對比

關於select與epoll

兩種IO模型,都屬於多路IO就緒通知,提供了對大量文件描述符就緒檢查的高性能方案,只不過實現方式有所不同:

select:

一個select()系統調用來監視包含多個文件描述符的數組,當select返回,該數組中就緒的文件描述符便會被內核修改標誌位。

select的跨平臺做的很好,幾乎每個平臺都支持。

select缺點有以下三點:

  1. 單個進程能夠監視的文件描述符的數量存在最大限制

  2. select()所維護的存儲大量文件描述符的數據結構,隨着文件描述符數量的增長,其在用戶態和內核的地址空間的複製所引發的開銷也會線性增長

  3. 由於網絡響應時間的延遲使得大量TCP連接處於非活躍狀態,但調用select()還是會對所有的socket進行一次線性掃描,會造成一定的開銷

poll:

poll是unix沿用select自己重新實現了一遍,唯一解決的問題是poll沒有最大文件描述符數量的限制

epoll:

epoll帶來了兩個優勢,大幅度提升了性能:

  1. 基於事件的就緒通知方式,select/poll方式,進程只有在調用一定的方法後,內核纔會對所有監視的文件描述符進行掃描,而epoll事件通過epoll_ctl()註冊一個文件描述符,一旦某個文件描述符就緒時,內核會採用類似call back的回調機制,迅速激活這個文件描述符,epoll_wait()便會得到通知

  2. 調用一次epoll_wait()獲得就緒文件描述符時,返回的並不是實際的描述符,而是一個代表就緒描述符數量的值,拿到這些值去epoll指定的一個數組中依次取得相應數量的文件描述符即可,這裏使用內存映射(mmap)技術,避免了複製大量文件描述符帶來的開銷

當然epoll也有一定的侷限性,epoll只有Linux2.6纔有實現,而其他平臺都沒有,這和apache這種優秀的跨平臺服務器,顯然是有些背道而馳了。


Apache與Nginx:

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方式優勢會更加明顯。

面對的場景:

我們反觀一下網站目前的狀況和場景:

%E7%BD%91%E7%AB%99rt.png
%E7%BD%91%E7%AB%99tcp.png



上圖可以看到,rt監控當中,由於我們是動態網站,網站的大部分內容都需要動態獲取、計算、輸出,因此響應時間普遍位於100毫秒以上,響應時間較長,服務器必將創建更多的連接處理這些請求,而tcp監控當中,可以看到由於TCP協議特有的特性,服務端主動關閉一個連接,連接會進入等待超時的狀態,且此狀態會持續2MSL(即兩倍的數據包最大生存時間,這個時 間長短跟操作系統有關,一般會在1-4分鐘),因此服務器端會保留一定量time_wait連接,管理大量的連接也會對服務器造成一定的成本,而epoll在多連接併發處理以及管理這兩方面,都較於select具有很大的優勢。這也正是高併發、高連接的互聯網網站大量使用Nginx服務器的原因所在。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章