1. 程式人生 > >apache和nginx的性能分析

apache和nginx的性能分析

apache和nginx的性能分析

****簡單分析nginx與apache的性能*****
"Apache就像Microsoft的Word,它有一百萬個選項,但你只需要做六個。Nginx只做了這六件事,但他做的這六件事中有五件事比Apache快50倍"

常見的web服務器:nginx apache lighttpd tomcat jetty iis
web服務器的基本功能:基於REST架構風格,以統一資源描述符或者統一資源定位符作為溝通依據,通過HTTP為瀏覽器等客戶端程序提供各種網絡服務
tomcat和jetty面向java語言,先天就是重量級的WEB服務器,他的性能與nginx沒有可比性,iis只能在windows上運行,windows作為服務器在穩定性上與其它一些性能上都不如類UNIX操作系統
apache有很多優點,如穩定,開源,跨平臺等,但是它是一個重量級的,不支持高並發的web服務器
nginx和lighttpd一樣,都是輕量級的,高性能的web服務器,歐美開發者更鐘愛lighttpd,而國內的開發者更青睞nginx

###nginx###
nginx是一個跨平臺的web服務器,可以運行在linux,freebsd,solaris,aix,macos,windows等操作系統上,並且它可以使用當前操作系統特有的一些高效API來提高性能
nginx設計的目的是為了解決c10k問題
對於處理大規模並發連接,他支持linux上的epoll,windows無epoll,所以在windows下的nginx服務器會損失性能

對於linux,nginx支持其獨有的sendfile系統調用,這個系統調用可以高效的把硬盤上的數據發送到網絡上,不需要先把硬盤數據復制到用戶態內存上再發送,這極大減少了內核態與用戶態數據間的復制動作
nginx使用基於事件驅動的架構能夠並發處理百萬級別的TCP連接:
事件驅動架構:由一些事件發生源來產生事件,由一個或者多個事件收集器來收集,分發事件然後許多事件處理器會註冊自己感興趣的事件,同時會消費這些事件
nginx采用完全的事件驅動架構來處理業務,這與傳統的WEB服務器(如apache)是不同的。對於傳統的web服務器而言,采用的所謂事件驅動往往局限在TCP連接建立,關閉事件上,一個連接建立以後,在其關閉之前的所有操作都不再是事件驅動,這時會退化成按序執行每個操作的批處理模式,這樣每個請求在連接建立以後都始終占用著系統資源,知道連接關閉才會釋放資源,整個事件消費進程只是在等待某個事件而已,造成了服務器資源的極大浪費,影響了系統可以處理的並發連接數,這種傳統的web服務器往往會把一個進程或者線程作為事件消費者,當一個請求的事件被該進程處理時,直到這個請求處理結束時進程資源都將被這一個請求所占用,nginx不會使用進程或者線程作為事件消費者,事件消費者只能是某個模塊,只有事件收集、分發器才有資格占用進程資源,他們會在分發某個事件時調用事件消費模塊使用當前占用的進程資源。
傳統web服務器與nginx間的重要區別:前者是每個事件消費者獨占一個進程資源,後者的事件消費者只是被事件分發者進程短期調用而已。這種設計使的網絡性能,用戶感知的請求時延都得到了提升,每個用戶的請求所產生的事件會及時響應,整個服務器的網絡吞吐量都會由於事件的及時響應而增大。但這也會帶來一個重要的弊端,即每個事件消費者都不能由阻塞行為,否則將會由於長時間占用事件分發者進程而導致其他事件得不到及時響應。尤其是每個事件消費者不可以讓進程轉變為休眠狀態或等待狀態

請求的多階段異步處理:
請求的多階段異步處理只能基於事件驅動架構實現,把一個請求的處理過程按照事件的觸發方式劃分為多個階段,每個事件都可以由事件收集、分發器來觸發
異步處理和多階段是相輔相成的,只有把請求分為多個階段,才有所謂的異步處理。也就是說,當一個事件被分發到事件消費者中處理時,事件消費者處理完這個事件只相當於處理完1個請求的某個階段,等待內核的通知才可以處理下一個階段,即當下一次事件出現時,epoll等事件分發器將會獲取到通知,在繼續地調用事件消費者處理請求,這樣每個階段中的事件消費者都不清楚本次完整的操作究竟什麽時候才能完成,只能異步被動的等待下一個事件的通知
請求的多階段異步處理配合事件驅動架構,將會極大的提高網絡性能,同時使得每個進程都能全力運轉,不會或者盡量少的出現進程休眠狀態。

管理進程,多工作進程設計:
nginx采用一個master管理進程,多個worker工作進程的設計方式:
(1)利用多核系統的並發處理能力:nginx中的所有worker工作進程都是完全平等的,這提高了網絡性能,降低了請求的時延
(2)負載均衡:一個請求到來時更容易被分配到負載較輕的worker工作進程中處理,這降低了請求的時延,並在一定程度上提高網絡性能
(3)管理進程會負責監控工作進程的狀態,並負責管理其行為:管理進程不會占用多少系統資源,他只是用來啟動,停止,監控或者使用其他行為來監控工作進程。首先,這提高了系統的可靠性,當工作進程出現問題時,管理進程可以啟動新的工作進程來避免系統性能的下降,其次,管理進程支持nginx服務運行中的程序升級、配置項的修改等操作,這種設計使得動態可擴展性、動態定制性、動態可進化性較容易實現

熱部署:master管理進程與worker工作進程的分離設計,使nginx能夠提供在7*24小時不間斷服務的前提下,升級nginx的可執行文件,也支持不停止服務就更新配置項,更換日誌文件等功能

如何解決驚群問題:
只有打開了accept_mutex鎖,才可以解決驚群問題
驚群問題:master進程開始監聽web端口,fork出多個worker子進程,這些子進程開始同時監聽同一個web端口。一般情況下,有多少cpu核心,就會配置多少個worker子進程,這樣所有的worker子進程都在承擔著web服務器的角色,在這種情況下,就可以利用每一個cpu核心可以並發工作的特性,充分發揮多核的作用。假設這樣一個情景:沒有用戶連入服務器,某一時刻恰好所有的worker子進程都休眠且等待新連接的系統調用(如epoll_wait),這時有一個用戶向服務器發起了連接,內核在收到TCP的SYN包時,會激活所有的休眠worker子進程,當然,此時只有最先開始執行accpet的子進程可以成功建立新連接,而其他worker子進程都會accept失敗,這些accept失敗的子進程被內核喚醒是不必要的,他們喚醒後的執行很可能是多余的,那麽這一時刻他們占用的本不需要占用的系統資源,引發了不必要的進程上下文切換,增加了系統開銷
“驚群”是多個子進程同時監聽同一個端口引起的,如果同一時刻只能由唯一一個worker子進程監聽web端口,就不會發生驚群了,此時新連接事件只能喚醒唯一正在監聽端口的worker進程
如何實現負載均衡:
只有打開了accept_mutex鎖,才可以解決worker子進程間的負載均衡

支持mmap內存映射:
傳統的web服務器,都是先將磁盤的內容縣復制到內核緩存中,然後從內核緩存中復制一份到web服務器上,mmap就是讓內核緩存與磁盤進行映射,web服務器直接復制就可以,不用將磁盤上的內容復制到內核緩存中

###apache
apache的三種工作模式:
(1)prefork:prefork模式算是很古老但是非常穩定的apache模式。apache在啟動之初,就先fork一些子進程,然後等待請求進來。之所以這樣做,是為了減少頻繁創建和銷毀進程的開銷。每個子進程只有一個線程,在一個時間點內,只能處理一個請求
優點:成熟穩定,兼容所有新老模塊。同時,不需要擔心線程安全問題
缺點:一個進程相對占用更多的系統資源,消耗更多的內存。而且,他並不擅長處理高並發請求,在這種場景下,它會將請求放進隊列裏,一直等到有可用進程,請求才會被處理
(2)worker:和prefork模式相比,worker使用了多線程和多進程的混合模式,worker模式也同樣會先預派生一些子進程,然後每個子進程創建一些線程,同時包括一個監控線程,每個請求過來會被分配到一個線程來服務。線程比進程更輕量,因為線程是通過共享父進程的內存空間,因此,內存的占用會減少一些,在高並發的場景下會比prefork有更多可用的線程,表現更優秀些
優點:占用更少的內存,高並發下表現優秀
缺點:必須考慮線程安全的問題,因為多個子線程是共享父進程的內存地址的。如果使用keep-alive長連接的方式,某個線程會一直被占據,也許中間幾乎沒有請求,需要一直等待到超時才會被釋放。如果太多的線程,被這樣占據,也會導致在高並發場景下的無服務線程可用
(3)event:它和worker模式很像,不同的是它解決了keep-alive長連接的時候占用線程資源被浪費的問題,在event模式中,會有一些專門的線程來管理這些keep-alive類型的線程,當有真實請求過來的時候,將請求傳遞給服務器的線程,執行完畢後,又允許它釋放,增強了在高並發場景下的請求處理




###epoll與select、poll的比較
假設有100萬用戶同時與一個進程保持著TCP連接,而每一個時刻只有幾十個或者幾百個TCP連接是活躍的,也就是說,在每一時刻,進程只需要處理這100萬連接中的一小部分連接。select和poll在每次收集事件時,都把這100萬連接的套接字傳給操作系統(這首先時用戶態內存到內核態內存的大量復制),而由操作系統內核尋找這些連接上有沒有未處理的事件,將是巨大的資源浪費,因此他們最多只能處理幾千個並發連接。而epoll沒有這樣做,它在linux內核系統內申請了一個簡易的文件系統,把原先的一個select或者poll調用分成了三個部分:調用epoll_create建立一個epoll對象、調用epoll_ctl向epoll對象添加這100萬連接的套接字、調集epoll_wait收集發生事件的連接。這樣,只需要在進程啟動時建立一個epoll對象,並在需要的時候向他添加或者刪除連接就可以了,因此,在實際收集事件時,epoll_wait的效率就會非常高,因為調用epoll_wait時並沒有向它傳遞這100萬個連接,內核也不需要去遍歷全部的連接

###cgi與fastcgi
cgi為每一個請求產生一個唯一的進程,從一個請求到另一個請求,內存和其他的信息將全部丟失
fastcgi使用了能夠處理多個請求的持續過程,而不是針對每個請求都產生新的進程


apache和nginx的性能分析