1.先從各自使用的多路復用IO模型說起:
select模型:(apache使用,由於受模塊等限制,用的不多)
- 單個進程能夠 監視的文件描述符的數量存在最大限制
- select()所維護的 存儲大量文件描述符的數據結構 ,隨着文件描述符數量的增長,其在用戶態和內核的地址空間的復制所引發的開銷也會線性增長
- 由於網絡響應時間的延遲使得大量TCP連接處於非活躍狀態,但調用select()還是會對 所有的socket進行一次線性掃描 ,會造成一定的開銷
poll:
poll是unix沿用select自己重新實現了一遍,唯一解決的問題是poll 沒有最大文件描述符數量的限制
epoll模型:(nginx使用)
epoll帶來了兩個優勢,大幅度提升了性能:
- 基於事件的就緒通知方式 ,select/poll方式,進程只有在調用一定的方法后,內核才會對所有監視的文件描述符進行掃描,而epoll事件通過epoll_ctl()注冊一個文件描述符,一旦某個文件描述符就緒時,內核會采用類似call back的回調機制,迅速激活這個文件描述符,epoll_wait()便會得到通知
- 調用一次epoll_wait()獲得就緒文件描述符時,返回的並不是實際的描述符,而是一個代表就緒描述符數量的值,拿到這些值去epoll指定的一個數組中依次取得相應數量的文件描述符即可,這里使用內存映射(mmap)技術, 避免了復制大量文件描述符帶來的開銷
當然epoll也有一定的局限性, epoll只有Linux2.6才有實現 ,而其他平台都沒有,這和apache這種優秀的跨平台服務器,顯然是有些背道而馳了。
簡單來說epoll是select的升級版,單進程管理的文件描述符沒有最大限制。但epoll只有linux平台可使用。作為跨平台的Apache沒有使用。
2.再看一下Apache常用的兩種並發策略:
1) perfork模式的工作原理:
當Apache被啟動時,Apache會自動創建StartServers個進程,並且盡力將空閑進程數保持在MinSpareServers和MaxSpareServers之間。
如果空閑進程小於MinSpareServers,Apache將會以大約每秒1個的速度新建進程。
如果空閑進程小於MaxSpareServers,Apache將會刪除多余的空閑進程,釋放服務器資源。
進程數的最大值由MaxClients控制,在Apache1.3中最大只能設置為256,但在Apache2.0中,可以通過在配置開頭增加ServerLimit項目來突破256的限制,此時必須MaxClients ≤ ServerLimit ≤ 20000
MaxRequestsPerChild用來控制每個進程在處理了多少次請求之后自動銷毀,這個參數可以設置為0表示無限(即不銷毀進程)
2) worker模式的工作原理:
由主控制進程生成“StartServers”個子進程,每個子進程中包含固定的ThreadsPerChild線程數,各個線程獨立地處理請求。
同樣,為了不在請求到來時再生成線程,MinSpareThreads和MaxSpareThreads設置了最少和最多的空閑線程數;而MaxClients設置允許的最大線程總數。
如果現有子進程中的線程總數不能滿足負載,控制進程將派生新的子進程。
每個子線程處理服務請求次數由MaxRequestPerChild定義。 缺省的設置值為0,即響應無限此請求。
默認生成3個子進程來處理請求。
兩種策略的缺陷:
perfork模式:每一個連接創建一個進程,每個進程內單線程。對於一個負載相對較高的網站來說,256的進程限制是不夠的,如果服務器已經達到256的極限,那么接下去的訪問就需要排隊,這也就是為什么某些服務器負載不高,但是訪問卻很慢的原因之一。
worker模式:也是多進程處理,也會創建多個進程和多個線程,如果進程數達到管理員設置的閥值,則會拒絕新的請求。
兩種模式都會創建多個進程或線程,而每個進程或線程都會為其分配cpu和內存(線程要比進程小的多,所以worker支持比perfork高的並發),並發過大會榨干服務器資源。
3.Nginx的工作原理:
Nginx並不會為每一個的web請求創建新的進程,相反,管理員可以配置Nginx主進程的工作進程的數量(一個常見的做法是為每一個CPU配置一個工作進程)。所有這些進程都是單線程的。
每一個工作進程可以處理數千個並發的請求。它通過一個線程來異步非阻塞的完成了這些工作(epoll),而沒有使用多線程的編程模型。
nginx的優勢:
采用單線程來異步非阻塞處理請求,不會為每個請求分配cpu和內存資源,節省了大量資源,同時也減少了大量的CPU的上下文切換。所以才使得Nginx支持更高的並發。