天天看點

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使用了能夠處理多個請求的持續過程,而不是針對每個請求都産生新的程序

本文轉自 小新銳 51CTO部落格,原文連結:http://blog.51cto.com/13407306/2056592,如需轉載請自行聯系原作者