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使用了可以處理多個請求的持續過程,而不是針對每一個請求都產生新的進程
 
java

相關文章
相關標籤/搜索