原文:http://www.aosabook.org/en/nginx.html
作者:?Andrew Alexeev
nginx(發(fā)音"engine x")是俄羅斯軟件工程師Igor Sysoev開發(fā)的免費(fèi)開源web服務(wù)器軟件。nginx于2004年發(fā)布,聚焦于高性能,高并發(fā)和低內(nèi)存消耗問題。并且具有多種web服務(wù)器功能特性:負(fù)載均衡,緩存,訪問控制,帶寬控制,以及高效整合各種應(yīng)用的能力,這些特性使nginx很適合于現(xiàn)代網(wǎng)站架構(gòu)。目前,nginx已經(jīng)是互聯(lián)網(wǎng)上第二流行的開源web服務(wù)器軟件。
和十年前相比,目前的互聯(lián)網(wǎng)已經(jīng)難以想象的廣泛應(yīng)用和普及。從NCSA用Apache搭的web服務(wù)器提供的可點(diǎn)擊的文本HTML,已然進(jìn)化成超過20億人在線的通信媒介。隨著永久在線的個(gè)人電腦,移動(dòng)終端以及平板電腦的增多,互聯(lián)網(wǎng)在快速變化,經(jīng)濟(jì)系統(tǒng)也完全數(shù)字有線化。提供實(shí)時(shí)可用信息和娛樂的在線服務(wù)變得更加復(fù)雜精巧。在線業(yè)務(wù)的安全需求也急劇變化。網(wǎng)站比從前更加復(fù)雜,需要在工程上做的更具有健壯性和可伸縮性。
并發(fā)總是網(wǎng)站架構(gòu)最大的挑戰(zhàn)之一。由于web服務(wù)的興起,并發(fā)的數(shù)量級(jí)在不斷增長。熱門網(wǎng)站為幾十萬甚至幾百萬的同時(shí)在線用戶提供服務(wù)并不尋常。十年前,并發(fā)的主要原因是由于客戶端接入速度慢--用戶使用ADSL或者撥號(hào)商務(wù)?,F(xiàn)在,并發(fā)是由移動(dòng)終端和新應(yīng)用架構(gòu)所帶來,這些應(yīng)用通?;诔志眠B接來為客戶端提供新聞,微博,通知等服務(wù)。另一個(gè)重要的因素就是現(xiàn)代瀏覽器行為變了,他們?yōu)g覽網(wǎng)站的時(shí)候會(huì)同時(shí)打開4到6個(gè)連接來加快頁面加載速度。
舉例說明一下慢客戶端的問題,假設(shè)一個(gè)Apache網(wǎng)站產(chǎn)生小于100KB的響應(yīng)--包含文本或圖片的網(wǎng)頁。生成這個(gè)頁面可能需要1秒鐘,但是如果網(wǎng)速只有80kbps(10KB/s),需要花10秒才能把這個(gè)頁面發(fā)送到客戶端?;旧希瑆eb服務(wù)器相對(duì)快速的推送100KB數(shù)據(jù),然后需要等待10秒發(fā)送數(shù)據(jù)之后才能關(guān)閉連接。那么現(xiàn)在如果有1000個(gè)同時(shí)連接的客戶端請(qǐng)求相同的頁面,那么如果為每個(gè)客戶端分配1MB內(nèi)存,就需要1000MB內(nèi)存來為這1000個(gè)客戶端提供這個(gè)頁面。實(shí)際上,一個(gè)典型的基于Apache的web服務(wù)器通常為每個(gè)連接分配1MB內(nèi)存,而移動(dòng)通信的有效速度也通常是幾十kbps。雖然借助于增加操作系統(tǒng)內(nèi)核socket緩沖區(qū)大小,可以優(yōu)化發(fā)送數(shù)據(jù)給慢客戶端的場景,但是這并不是一個(gè)常規(guī)的解決方案,并且會(huì)帶來無法預(yù)料的副作用。
隨著持久連接的使用,并發(fā)處理的問題更加明顯。為了避免新建HTTP連接所帶來的延時(shí),客戶端需要保持連接,這樣web服務(wù)器就需要為每個(gè)連接上的客戶端分配一定數(shù)量的內(nèi)存。
因此,為了處理持續(xù)增長的用戶帶來的負(fù)載和更高量級(jí)的并發(fā),網(wǎng)站需要大量高效的組件。而另一方面,web服務(wù)器軟件運(yùn)行在諸如硬件(CPU,內(nèi)存,磁盤),網(wǎng)絡(luò)帶寬,應(yīng)用和數(shù)據(jù)存儲(chǔ)架構(gòu)等之上,這些基礎(chǔ)設(shè)施顯然也很重要。因而,隨著同時(shí)在線數(shù)和每秒請(qǐng)求數(shù)的增長,web服務(wù)器性能也應(yīng)該能夠非線性擴(kuò)展。
Apache不再適用?
Apache web服務(wù)器軟件發(fā)源于1990年代,目前在互聯(lián)網(wǎng)網(wǎng)站上占有率第一。Apache的架構(gòu)適合當(dāng)時(shí)的操作系統(tǒng)和硬件,并且也符合當(dāng)時(shí)的互聯(lián)網(wǎng)狀況:一個(gè)網(wǎng)站通常使用一臺(tái)物理服務(wù)器運(yùn)行一個(gè)Apache實(shí)例。2000年之后,顯然這種單服務(wù)器模型已經(jīng)無法簡單擴(kuò)展來滿足日益增長的web服務(wù)需求。雖然Apache為新功能開發(fā)提供了堅(jiān)實(shí)的基礎(chǔ),但他為每個(gè)新連接派生一個(gè)進(jìn)程的做法(譯注:Apache從2.4版本起已經(jīng)支持事件模型),不適合網(wǎng)站的非線性擴(kuò)展。最終,Apache成為一個(gè)通用的web服務(wù)器軟件,聚焦于功能多樣化,第三方擴(kuò)展開發(fā),以及web應(yīng)用開發(fā)的通用性。然而,當(dāng)硬件成本越來越低,每個(gè)連接消耗的CPU和內(nèi)存越來越多,使用這樣功能繁多的單一軟件不再具有可伸縮性。
因而,當(dāng)服務(wù)器硬件、操作系統(tǒng)和網(wǎng)絡(luò)設(shè)施不再成為網(wǎng)站增長的主要限制因素時(shí),網(wǎng)站開發(fā)者開始尋求更高效的手段來架設(shè)web服務(wù)器。大約十年前,著名軟件工程師Daniel Kegel提出:“是時(shí)候讓web服務(wù)器支持同時(shí)處理10000客戶端了”,并且預(yù)言了現(xiàn)在稱為云服務(wù)的技術(shù)。Kegel的C10K設(shè)想明顯推動(dòng)了許多人嘗試解決這個(gè)問題--通過優(yōu)化web服務(wù)器軟件來支持大規(guī)??蛻舳诉B接的并發(fā)處理,nginx是其中做的最成功者之一。
為了解決10000個(gè)并發(fā)連接的C10K問題,nginx基于一個(gè)完全不同的架構(gòu)—更適合每秒同時(shí)連接數(shù)和請(qǐng)求數(shù)非線性增長。Nginx基于事件模型,而沒有模仿Apache為每個(gè)請(qǐng)求派生新進(jìn)程或線程的做法。最終結(jié)果就是即使負(fù)載增加了,內(nèi)存和CPU使用事件始終保持可預(yù)期。Nginx使用普通的硬件就能在一個(gè)服務(wù)器上處理數(shù)萬的并發(fā)連接。
Nginx的第一個(gè)版本發(fā)布之后,一般被用來同Apache一同部署,HTML、CSS、JavaScript腳本和圖片等靜態(tài)內(nèi)容由nginx處理,來降低Apache應(yīng)用服務(wù)器的并發(fā)和延時(shí)。隨著開發(fā)演進(jìn)的過程,nginx增加了FastCGI、uswge和SCGI等協(xié)議的支持,以及對(duì)分布式內(nèi)存對(duì)象緩存系統(tǒng)如memcached的支持。也增加了其他有用的功能,例如支持負(fù)載均衡和緩存的反向代理。這些附加功能使nginx成為一個(gè)高效的工具集,用于構(gòu)建可伸縮的web基礎(chǔ)設(shè)施。
2012年2月,Apache 2.4.x版本發(fā)布。雖然增加了新的并發(fā)處理核心模塊和代理模塊,用于加強(qiáng)可伸縮性和性能,但要說性能、并發(fā)能力和資源利用率是否能趕上或超過純事件驅(qū)動(dòng)模型的web服務(wù)器還為時(shí)尚早。Apache新版本具有了更好的性能值得高興,對(duì)于nginx+Apache的web網(wǎng)站架構(gòu),雖然這能夠緩解后端潛在的瓶頸,但并不能解決全部問題。
nginx有更多的優(yōu)點(diǎn)嗎?
部署nginx最關(guān)鍵的好處就是能夠高性能高效的處理高并發(fā)。同時(shí),還有更多有意思的好處。
最近幾年,web架構(gòu)擁抱解耦的理念并且將應(yīng)用層設(shè)施從web服務(wù)器中分離。雖然現(xiàn)在僅僅是將原先基于LAMP(Linux, Apache, MySQL, PHP, Python or Perl)所構(gòu)建的網(wǎng)站,變?yōu)榛贚EMP(E表示Engine x)的。但是,越來越多的實(shí)踐是將web服務(wù)器推入基礎(chǔ)設(shè)施的邊緣,并且用不同的方法整合這些相同或更新的應(yīng)用和數(shù)據(jù)庫工具集。
Nginx很適合做這些工作。他提供了必要的關(guān)鍵功能用于方便將下列功能從應(yīng)用層剝離到更高效的邊緣web服務(wù)器層:并發(fā)、長連接處理、SSL,靜態(tài)內(nèi)容、壓縮和緩存、連接和請(qǐng)求限速,以及HTTP媒體流等。Nginx同時(shí)也允許直接整合memcached、Redis或者其他的NoSQL解決方案,增強(qiáng)為處理大規(guī)模并發(fā)用戶的性能。
隨著現(xiàn)代編程語言和開發(fā)包廣泛使用,越來越多的公司改變了應(yīng)用開發(fā)和部署的方式。Nginx已經(jīng)成為這些改變范例之中的最重要的部件之一,并且已經(jīng)幫助許多公司在預(yù)算內(nèi)快速啟動(dòng)和開發(fā)他們的web服務(wù)。
Nginx開發(fā)始于2002年,2004年基于2-clause BSD授權(quán)正式對(duì)外發(fā)布。自發(fā)布起,Nginx用戶就在不斷增長,并且貢獻(xiàn)提議,提交bug報(bào)告、建議和評(píng)測報(bào)告,這極大的幫助和促進(jìn)了整個(gè)社區(qū)的發(fā)展。
Nginx代碼完全用C語言從頭寫成,已經(jīng)移植到許多體系結(jié)構(gòu)和操作系統(tǒng),包括:Linux、FreeBSD、Solaris、Mac OS X、AIX以及Microsoft Windows。Nginx有自己的函數(shù)庫,并且除了zlib、PCRE和OpenSSL之外,標(biāo)準(zhǔn)模塊只使用系統(tǒng)C庫函數(shù)。而且,如果不需要或者考慮到潛在的授權(quán)沖突,可以不使用這些第三方庫。
談?wù)勱P(guān)于Windows版本nginx。當(dāng)nignx在Windows環(huán)境下工作時(shí),Windows版本的nginx更像是概念驗(yàn)證版本,而不是全功能移植。這是由于目前nginx和Windows內(nèi)核架構(gòu)之間交互的某些限制導(dǎo)致。Windows版本ngnix已知的問題包括:低并發(fā)連接數(shù)、性能降低、不支持緩存和帶寬策略。未來Windows版本的nginx的功能會(huì)更接近主流版本。
傳統(tǒng)基于進(jìn)程或線程的模型使用單獨(dú)的進(jìn)程或線程處理并發(fā)連接,因而會(huì)阻塞于網(wǎng)絡(luò)或I/O操作。根據(jù)不同的應(yīng)用,就內(nèi)存和CPU而言,這是非常低效的。派生進(jìn)程或線程需要準(zhǔn)備新的運(yùn)行環(huán)境,包括在內(nèi)存上分配堆和棧、生成一個(gè)新的運(yùn)行上下文。創(chuàng)建這些東西還需要額外的CPU時(shí)間,而且過度的上下文切換引起的線程抖動(dòng)最終會(huì)導(dǎo)致性能低下。所有這些復(fù)雜性在如Apache web服務(wù)器的老架構(gòu)上一覽無遺。在提供豐富的通用應(yīng)用功能和優(yōu)化服務(wù)器資源使用之間需要做一個(gè)權(quán)衡。
最早的時(shí)候,nginx希望為動(dòng)態(tài)增長的網(wǎng)站獲得更好的性能,并且密集高效的使用服務(wù)器資源,所以其使用了另外一個(gè)模型。受不斷發(fā)展的在不同操作系統(tǒng)上開發(fā)基于事件模型的技術(shù)驅(qū)動(dòng),最終一個(gè)模塊化,事件驅(qū)動(dòng),異步,單線程,非阻塞架構(gòu)成為nginx代碼的基礎(chǔ)。
Nginx大量使用多路復(fù)用和事件通知,并且給不同的進(jìn)程分配不同的任務(wù)。數(shù)量有限的工作進(jìn)程(Worker)使用高效的單線程循環(huán)處理連接。每個(gè)worker進(jìn)程每秒可以處理數(shù)千個(gè)并發(fā)連接、請(qǐng)求。
代碼結(jié)構(gòu)
Nginx worker的代碼包含核心和功能模塊。核心負(fù)責(zé)維護(hù)一個(gè)緊湊的事件處理循環(huán),并且在請(qǐng)求處理的每個(gè)階段執(zhí)行對(duì)應(yīng)的模塊代碼段。模塊完成了大部分展現(xiàn)和應(yīng)用層功能。包括從網(wǎng)絡(luò)和存儲(chǔ)設(shè)備讀取、寫入,轉(zhuǎn)換內(nèi)容,進(jìn)行輸出過濾,SSI(server-side include)處理,或者如果啟用代理則轉(zhuǎn)發(fā)請(qǐng)求給后端服務(wù)器。
nginx模塊化的架構(gòu)允許開發(fā)者擴(kuò)展web服務(wù)器的功能,而不需要修改nginx核心。Nginx模塊可分為:核心、事件模塊,階段處理器,協(xié)議、變量處理器,過濾器,上游和負(fù)載均衡器等。目前,nginx不支持動(dòng)態(tài)加載模塊,即模塊代碼是和nginx核心代碼一起編譯的。模塊動(dòng)態(tài)加載和ABI已經(jīng)計(jì)劃在將來的某個(gè)版本開發(fā)。更多關(guān)于不同模塊角色的詳細(xì)信息可在14.4章找到。
Nginx在Linux、Solaris和BSD系統(tǒng)上使用kqueue、epoll和event ports等技術(shù),通過事件通知機(jī)制來處理網(wǎng)絡(luò)連接和內(nèi)容獲取,包括接受、處理和管理連接,并且大大增強(qiáng)了磁盤IO性能。目的在于盡可能的提供操作系統(tǒng)建議的手段,用于從網(wǎng)絡(luò)進(jìn)出流量,磁盤操作,套接字讀取和寫入,超時(shí)等事件中及時(shí)異步地獲取反饋。Nginx為每個(gè)基于Unix的操作系統(tǒng)大量優(yōu)化了這些多路復(fù)用和高級(jí)I/O操作的方法。
圖14.1展示了nginx架構(gòu)的高層設(shè)計(jì)。
更多建議: