2021-02-07 分類: 網(wǎng)站建設(shè)
1 反向代理
1.1 概念
反向代理(Reverse Proxy)方式是指以代理服務(wù)器來接受internet上的連接請(qǐng)求,然后將請(qǐng)求轉(zhuǎn)發(fā)給內(nèi)部網(wǎng)絡(luò)上的服務(wù)器,并將從服務(wù)器上得到的結(jié)果返回給internet上請(qǐng)求連接的客戶端,此時(shí)代理服務(wù)器對(duì)外就表現(xiàn)為一個(gè)服務(wù)器。
舉個(gè)例子,比如我想訪問 http://www.test.com/readme,但www.test.com上并不存在readme頁面,于是他是偷偷從另外一臺(tái)服務(wù)器上取回來,然后作為自己的內(nèi)容返回用戶,但用戶并不知情。這里所提到的 www.test.com 這個(gè)域名對(duì)應(yīng)的服務(wù)器就設(shè)置了反向代理功能。
結(jié)論就是,反向代理服務(wù)器對(duì)于客戶端而言它就像是原始服務(wù)器,并且客戶端不需要進(jìn)行任何特別的設(shè)置。客戶端向反向代理的命名空間(name-space)中的內(nèi)容發(fā)送普通請(qǐng)求,接著反向代理服務(wù)器將判斷向何處(原始服務(wù)器)轉(zhuǎn)交請(qǐng)求,并將獲得的內(nèi)容返回給客戶端,就像這些內(nèi)容原本就是它自己的一樣。
正向代理,既然有反向代理,就肯定有正向代理。什么叫正向代理呢?
正向代理(Forward Proxy)通常都被簡(jiǎn)稱為代理,就是在用戶無法正常訪問外部資源,比方說受到GFW的影響無法訪問twitter的時(shí)候,我們可以通過代理的方式,讓用戶繞過防火墻,從而連接到目標(biāo)網(wǎng)絡(luò)或者服務(wù)。
正向代理的工作原理就像一個(gè)跳板,比如:我訪問不了google.com,但是我能訪問一個(gè)代理服務(wù)器A,A能訪問google.com,于是我先連上代理服務(wù)器A,告訴他我需要google.com的內(nèi)容,A就去取回來,然后返回給我。從網(wǎng)站的角度,只在代理服務(wù)器來取內(nèi)容的時(shí)候有一次記錄,有時(shí)候并不知道是用戶的請(qǐng)求,也隱藏了用戶的資料,這取決于代理告不告訴網(wǎng)站。
結(jié)論就是,正向代理是一個(gè)位于客戶端和原始服務(wù)器(origin server)之間的服務(wù)器。為了從原始服務(wù)器取得內(nèi)容,客戶端向代理發(fā)送一個(gè)請(qǐng)求并指定目標(biāo)(原始服務(wù)器),然后代理向原始服務(wù)器轉(zhuǎn)交請(qǐng)求并將獲得的內(nèi)容返回給客戶端。
反向代理VS正向代理:
正向代理示意圖
反向代理示意圖
1.2 工作流程
1.3 優(yōu)點(diǎn)
通常的代理服務(wù)器,只用于代理內(nèi)部網(wǎng)絡(luò)對(duì)Internet外部網(wǎng)絡(luò)的連接請(qǐng)求,客戶機(jī)必須指定代理服務(wù)器,并將本來要直接發(fā)送到Web服務(wù)器上的http請(qǐng)求發(fā)送到代理服務(wù)器中。不支持外部網(wǎng)絡(luò)對(duì)內(nèi)部網(wǎng)絡(luò)的連接請(qǐng)求,因?yàn)閮?nèi)部網(wǎng)絡(luò)對(duì)外部網(wǎng)絡(luò)是不可見的。當(dāng)一個(gè)代理服務(wù)器能夠代理外部網(wǎng)絡(luò)上的主機(jī),訪問內(nèi)部網(wǎng)絡(luò)時(shí),這種代理服務(wù)的方式稱為反向代理服務(wù)。此時(shí)代理服務(wù)器對(duì)外就表現(xiàn)為一個(gè)Web服務(wù)器,外部網(wǎng)絡(luò)就可以簡(jiǎn)單把它當(dāng)作一個(gè)標(biāo)準(zhǔn)的Web服務(wù)器而不需要特定的配置。不同之處在于,這個(gè)服務(wù)器沒有保存任何網(wǎng)頁的真實(shí)數(shù)據(jù),所有的靜態(tài)網(wǎng)頁或者CGI程序,都保存在內(nèi)部的Web服務(wù)器上。因此對(duì)反向代理服務(wù)器的攻擊并不會(huì)使得網(wǎng)頁信息遭到破壞,這樣就增強(qiáng)了Web服務(wù)器的安全性。
企業(yè)內(nèi)所有的網(wǎng)站共享一個(gè)在internet中注冊(cè)的IP地址,這些服務(wù)器分配私有地址,采用虛擬主機(jī)的方式對(duì)外提供服務(wù)。
反向代理就是通常所說的web服務(wù)器加速,它是一種通過在繁忙的web服務(wù)器和外部網(wǎng)絡(luò)之間增加一個(gè)高速的web緩沖服務(wù)器來降低實(shí)際的web服務(wù)器的負(fù)載的一種技術(shù)。反向代理是針對(duì)web服務(wù)器提高加速功能,作為代理緩存,它并不是針對(duì)瀏覽器用戶,而針對(duì)一臺(tái)或多臺(tái)特定的web服務(wù)器,它可以代理外部網(wǎng)絡(luò)對(duì)內(nèi)部網(wǎng)絡(luò)的訪問請(qǐng)求。
反向代理服務(wù)器會(huì)強(qiáng)制將外部網(wǎng)絡(luò)對(duì)要代理的服務(wù)器的訪問經(jīng)過它,這樣反向代理服務(wù)器負(fù)責(zé)接收客戶端的請(qǐng)求,然后到源服務(wù)器上獲取內(nèi)容,把內(nèi)容返回給用戶,并把內(nèi)容保存到本地,以便日后再收到同樣的信息請(qǐng)求時(shí),它會(huì)把本地緩存里的內(nèi)容直接發(fā)給用戶,以減少后端web服務(wù)器的壓力,提高響應(yīng)速度。因此Nginx還具有緩存功能。
(1)請(qǐng)求的統(tǒng)一控制,包括設(shè)置權(quán)限、過濾規(guī)則等;
(2)區(qū)分動(dòng)態(tài)和靜態(tài)可緩存內(nèi)容;
(3)實(shí)現(xiàn)負(fù)載均衡,內(nèi)部可以采用多臺(tái)服務(wù)器來組成服務(wù)器集群,外部還是可以采用一個(gè)地址訪問;
(4)解決Ajax跨域問題;
(5)作為真實(shí)服務(wù)器的緩沖,解決瞬間負(fù)載量大的問題;
Nginx有五大優(yōu)點(diǎn):模塊化、事件驅(qū)動(dòng)、異步、非阻塞、多進(jìn)程單線程。由內(nèi)核和模塊組成的,其中內(nèi)核完成的工作比較簡(jiǎn)單,僅僅通過查找配置文件將客戶端請(qǐng)求映射到一個(gè)location block,然后又將這個(gè)location block中所配置的每個(gè)指令將會(huì)啟動(dòng)不同的模塊去完成相應(yīng)的工作。
2 Nginx模塊
2.1 模塊劃分
Nginx的模塊從結(jié)構(gòu)上分為核心模塊、基礎(chǔ)模塊和第三方模塊:
核心模塊:HTTP模塊、EVENT模塊和MAIL模塊 基礎(chǔ)模塊:HTTP Access模塊、HTTP FastCGI模塊、HTTP Proxy模塊和HTTP Rewrite模塊, 第三方模塊:HTTP Upstream Request Hash模塊、Notice模塊和HTTP Access Key模塊。
Nginx的模塊從功能上分為如下四類:
Core(核心模塊):構(gòu)建nginx基礎(chǔ)服務(wù)、管理其他模塊。 Handlers(處理器模塊):此類模塊直接處理請(qǐng)求,并進(jìn)行輸出內(nèi)容和修改headers信息等操作。 Filters (過濾器模塊):此類模塊主要對(duì)其他處理器模塊輸出的內(nèi)容進(jìn)行修改操作,最后由Nginx輸出。 Proxies (代理類模塊):此類模塊是Nginx的HTTP Upstream之類的模塊,這些模塊主要與后端一些服務(wù)比如FastCGI等進(jìn)行交互,實(shí)現(xiàn)服務(wù)代理和負(fù)載均衡等功能。
Nginx的核心模塊主要負(fù)責(zé)建立nginx服務(wù)模型、管理網(wǎng)絡(luò)層和應(yīng)用層協(xié)議、以及啟動(dòng)針對(duì)特定應(yīng)用的一系列候選模塊。其他模塊負(fù)責(zé)分配給web服務(wù)器的實(shí)際工作:
(1) 當(dāng)Nginx發(fā)送文件或者轉(zhuǎn)發(fā)請(qǐng)求到其他服務(wù)器,由Handlers(處理模塊)或Proxies(代理類模塊)提供服務(wù); (2) 當(dāng)需要Nginx把輸出壓縮或者在服務(wù)端加一些東西,由Filters(過濾模塊)提供服務(wù)。
2.2 模塊處理
處理模塊以三種形式返回:
Nginx的負(fù)載均衡模塊采用兩種方法:
輪轉(zhuǎn)法,它處理請(qǐng)求就像紙牌游戲一樣從頭到尾分發(fā); IP哈希法,在眾多請(qǐng)求的情況下,它確保來自同一個(gè)IP的請(qǐng)求會(huì)分發(fā)到相同的后端服務(wù)器。
filters(過濾模塊)是經(jīng)典的“接力鏈表(CHAIN OF RESPONSIBILITY)”模型:一個(gè)filters(過濾模塊)被調(diào)用,完成其工作,然后調(diào)用下一個(gè)filters(過濾模塊),直到最后一個(gè)filters(過濾模塊)。
過濾模塊鏈的特別之處在于:
每個(gè)filters(過濾模塊)不會(huì)等上一個(gè)filters(過濾模塊)全部完成; 它能把前一個(gè)過濾模塊的輸出作為其處理內(nèi)容;有點(diǎn)像Unix中的流水線;
過濾模塊能以buffer(緩沖區(qū))為單位進(jìn)行操作,這些buffer一般都是一頁(4K)大小,當(dāng)然你也可以在nginx.conf文件中進(jìn)行配置。這意味著,比如,模塊可以壓縮來自后端服務(wù)器的響應(yīng),然后像流一樣的到達(dá)客戶端,直到整個(gè)響應(yīng)發(fā)送完成。
總之,過濾模塊鏈以流水線的方式高效率地向客戶端發(fā)送響應(yīng)信息。
客戶端發(fā)送HTTP請(qǐng)求 –> Nginx基于配置文件中的位置選擇一個(gè)合適的處理模塊 -> (如果有)負(fù)載均衡模塊選擇一臺(tái)后端服務(wù)器 –> 處理模塊進(jìn)行處理并把輸出緩沖放到第一個(gè)過濾模塊上 –> 第一個(gè)過濾模塊處理后輸出給第二個(gè)過濾模塊 –> 然后第二個(gè)過濾模塊又到第三個(gè) –> 依此類推 –> 最后把響應(yīng)發(fā)給客戶端。
下圖展示了Nginx模塊處理流程:
Nginx模塊處理流程
Nginx本身做的工作實(shí)際很少,當(dāng)它接到一個(gè)HTTP請(qǐng)求時(shí),它僅僅是通過查找配置文件將此次請(qǐng)求映射到一個(gè)location block,而此location中所配置的各個(gè)指令則會(huì)啟動(dòng)不同的模塊去完成工作,因此模塊可以看做Nginx真正的勞動(dòng)工作者。通常一個(gè)location中的指令會(huì)涉及一個(gè)handler模塊和多個(gè)filter模塊(當(dāng)然,多個(gè)location可以復(fù)用同一個(gè)模塊)。handler模塊負(fù)責(zé)處理請(qǐng)求,完成響應(yīng)內(nèi)容的生成,而filter模塊對(duì)響應(yīng)內(nèi)容進(jìn)行處理。
3 Nginx請(qǐng)求處理
Nginx在啟動(dòng)時(shí)會(huì)以daemon形式在后臺(tái)運(yùn)行,采用多進(jìn)程+異步非阻塞IO事件模型來處理各種連接請(qǐng)求。多進(jìn)程模型包括一個(gè)master進(jìn)程,多個(gè)worker進(jìn)程,一般worker進(jìn)程個(gè)數(shù)是根據(jù)服務(wù)器CPU核數(shù)來決定的。master進(jìn)程負(fù)責(zé)管理Nginx本身和其他worker進(jìn)程。如下圖:
Master進(jìn)程負(fù)責(zé)管理Nginx本身和其他worker進(jìn)程
Master-Worker模式
從上圖中可以很明顯地看到,4個(gè)worker進(jìn)程的父進(jìn)程都是master進(jìn)程,表明worker進(jìn)程都是從父進(jìn)程fork出來的,并且父進(jìn)程的ppid為1,表示其為daemon進(jìn)程。 需要說明的是,在nginx多進(jìn)程中,每個(gè)worker都是平等的,因此每個(gè)進(jìn)程處理外部請(qǐng)求的機(jī)會(huì)權(quán)重都是一致的。
Master進(jìn)程的作用是?
讀取并驗(yàn)證配置文件nginx.conf;管理worker進(jìn)程; Worker進(jìn)程的作用是? 每一個(gè)Worker進(jìn)程都維護(hù)一個(gè)線程(避免線程切換),處理連接和請(qǐng)求;注意Worker進(jìn)程的個(gè)數(shù)由配置文件決定,一般和CPU個(gè)數(shù)相關(guān)(有利于進(jìn)程切換),配置幾個(gè)就有幾個(gè)Worker進(jìn)程。
Nginx如何做到熱部署?
所謂熱部署,就是配置文件nginx.conf修改后,不需要stop Nginx,不需要中斷請(qǐng)求,就能讓配置文件生效!(nginx -s reload 重新加載/nginx -t檢查配置/nginx -s stop) 通過上文我們已經(jīng)知道worker進(jìn)程負(fù)責(zé)處理具體的請(qǐng)求,那么如果想達(dá)到熱部署的效果,可以想象: 方案一: 修改配置文件nginx.conf后,主進(jìn)程master負(fù)責(zé)推送給woker進(jìn)程更新配置信息,woker進(jìn)程收到信息后,更新進(jìn)程內(nèi)部的線程信息。 方案二: 修改配置文件nginx.conf后,重新生成新的worker進(jìn)程,當(dāng)然會(huì)以新的配置進(jìn)行處理請(qǐng)求,而且新的請(qǐng)求必須都交給新的worker進(jìn)程,至于老的worker進(jìn)程,等把那些以前的請(qǐng)求處理完畢后,kill掉即可。 Nginx采用的就是方案二來達(dá)到熱部署的!
Nginx如何做到高并發(fā)下的高效處理?
上文已經(jīng)提及Nginx的worker進(jìn)程個(gè)數(shù)與CPU綁定、worker進(jìn)程內(nèi)部包含一個(gè)線程高效回環(huán)處理請(qǐng)求,這的確有助于效率,但這是不夠的。 作為專業(yè)的程序員,我們可以開一下腦洞:BIO/NIO/AIO、異步/同步、阻塞/非阻塞... 要同時(shí)處理那么多的請(qǐng)求,要知道,有的請(qǐng)求需要發(fā)生IO,可能需要很長(zhǎng)時(shí)間,如果等著它,就會(huì)拖慢worker的處理速度。 Nginx采用了Linux的epoll模型,epoll模型基于事件驅(qū)動(dòng)機(jī)制,它可以監(jiān)控多個(gè)事件是否準(zhǔn)備完畢,如果OK,那么放入epoll隊(duì)列中,這個(gè)過程是異步的。worker只需要從epoll隊(duì)列循環(huán)處理即可。
Nginx掛了怎么辦?
Nginx既然作為入口網(wǎng)關(guān),很重要,如果出現(xiàn)單點(diǎn)問題,顯然是不可接受的。 答案是:Keepalived+Nginx實(shí)現(xiàn)高可用。 Keepalived是一個(gè)高可用解決方案,主要是用來防止服務(wù)器單點(diǎn)發(fā)生故障,可以通過和Nginx配合來實(shí)現(xiàn)Web服務(wù)的高可用。(其實(shí),Keepalived不僅僅可以和Nginx配合,還可以和很多其他服務(wù)配合) Keepalived+Nginx實(shí)現(xiàn)高可用的思路: 第一:請(qǐng)求不要直接打到Nginx上,應(yīng)該先通過Keepalived(這就是所謂虛擬IP,VIP) 第二:Keepalived應(yīng)該能監(jiān)控Nginx的生命狀態(tài)(提供一個(gè)用戶自定義的腳本,定期檢查Nginx進(jìn)程狀態(tài),進(jìn)行權(quán)重變化,,從而實(shí)現(xiàn)Nginx故障切換)
Keepalived+Nginx
Nginx架構(gòu)及工作流程圖:
Nginx架構(gòu)及工作流程圖
Nginx真正處理請(qǐng)求業(yè)務(wù)的是Worker之下的線程。worker進(jìn)程中有一個(gè)ngx_worker_process_cycle()函數(shù),執(zhí)行無限循環(huán),不斷處理收到的來自客戶端的請(qǐng)求,并進(jìn)行處理,直到整個(gè)Nginx服務(wù)被停止。
worker 進(jìn)程中,ngx_worker_process_cycle()函數(shù)就是這個(gè)無限循環(huán)的處理函數(shù)。在這個(gè)函數(shù)中,一個(gè)請(qǐng)求的簡(jiǎn)單處理流程如下:
3.1 多進(jìn)程處理模型
下面來介紹一個(gè)請(qǐng)求進(jìn)來,多進(jìn)程模型的處理方式:
首先,master進(jìn)程一開始就會(huì)根據(jù)我們的配置,來建立需要listen的網(wǎng)絡(luò)socket fd,然后fork出多個(gè)worker進(jìn)程。 其次,根據(jù)進(jìn)程的特性,新建立的worker進(jìn)程,也會(huì)和master進(jìn)程一樣,具有相同的設(shè)置。因此,其也會(huì)去監(jiān)聽相同ip端口的套接字socket fd。 然后,這個(gè)時(shí)候有多個(gè)worker進(jìn)程都在監(jiān)聽同樣設(shè)置的socket fd,意味著當(dāng)有一個(gè)請(qǐng)求進(jìn)來的時(shí)候,所有的worker都會(huì)感知到。這樣就會(huì)產(chǎn)生所謂的“驚群現(xiàn)象”。為了保證只會(huì)有一個(gè)進(jìn)程成功注冊(cè)到listenfd的讀事件,nginx中實(shí)現(xiàn)了一個(gè)“accept_mutex”類似互斥鎖,只有獲取到這個(gè)鎖的進(jìn)程,才可以去注冊(cè)讀事件。其他進(jìn)程全部accept 失敗。 最后,監(jiān)聽成功的worker進(jìn)程,讀取請(qǐng)求,解析處理,響應(yīng)數(shù)據(jù)返回給客戶端,斷開連接,結(jié)束。因此,一個(gè)request請(qǐng)求,只需要worker進(jìn)程就可以完成。
進(jìn)程模型的處理方式帶來的一些好處就是:進(jìn)程之間是獨(dú)立的,也就是一個(gè)worker進(jìn)程出現(xiàn)異常退出,其他worker進(jìn)程是不會(huì)受到影響的;此外,獨(dú)立進(jìn)程也會(huì)避免一些不需要的鎖操作,這樣子會(huì)提高處理效率,并且開發(fā)調(diào)試也更容易。
如前文所述,多進(jìn)程模型+異步非阻塞模型才是勝出的方案。單純的多進(jìn)程模型會(huì)導(dǎo)致連接并發(fā)數(shù)量的降低,而采用異步非阻塞IO模型很好的解決了這個(gè)問題;并且還因此避免的多線程的上下文切換導(dǎo)致的性能損失。
worker進(jìn)程會(huì)競(jìng)爭(zhēng)監(jiān)聽客戶端的連接請(qǐng)求:這種方式可能會(huì)帶來一個(gè)問題,就是可能所有的請(qǐng)求都被一個(gè)worker進(jìn)程給競(jìng)爭(zhēng)獲取了,導(dǎo)致其他進(jìn)程都比較空閑,而某一個(gè)進(jìn)程會(huì)處于忙碌的狀態(tài),這種狀態(tài)可能還會(huì)導(dǎo)致無法及時(shí)響應(yīng)連接而丟棄discard掉本有能力處理的請(qǐng)求。這種不公平的現(xiàn)象,是需要避免的,尤其是在高可靠web服務(wù)器環(huán)境下。
針對(duì)這種現(xiàn)象,Nginx采用了一個(gè)是否打開accept_mutex選項(xiàng)的值,ngx_accept_disabled標(biāo)識(shí)控制一個(gè)worker進(jìn)程是否需要去競(jìng)爭(zhēng)獲取accept_mutex選項(xiàng),進(jìn)而獲取accept事件。
ngx_accept_disabled值:nginx單進(jìn)程的所有連接總數(shù)的八分之一,減去剩下的空閑連接數(shù)量,得到的這個(gè)ngx_accept_disabled。 當(dāng)ngx_accept_disabled大于0時(shí),不會(huì)去嘗試獲取accept_mutex鎖,并且將ngx_accept_disabled減1,于是,每次執(zhí)行到此處時(shí),都會(huì)去減1,直到小于0。不去獲取accept_mutex鎖,就是等于讓出獲取連接的機(jī)會(huì),很顯然可以看出,當(dāng)空閑連接越少時(shí),ngx_accept_disable越大,于是讓出的機(jī)會(huì)就越多,這樣其它進(jìn)程獲取鎖的機(jī)會(huì)也就越大。不去accept,自己的連接就控制下來了,其它進(jìn)程的連接池就會(huì)得到利用,這樣,nginx就控制了多進(jìn)程間連接的平衡了。
3.2 一個(gè)簡(jiǎn)單的HTTP請(qǐng)求
從 Nginx 的內(nèi)部來看,一個(gè) HTTP Request 的處理過程涉及到以下幾個(gè)階段:
初始化 HTTP Request(讀取來自客戶端的數(shù)據(jù),生成 HTTP Request 對(duì)象,該對(duì)象含有該請(qǐng)求所有的信息)。 處理請(qǐng)求頭。 處理請(qǐng)求體。 如果有的話,調(diào)用與此請(qǐng)求(URL 或者 Location)關(guān)聯(lián)的 handler。 依次調(diào)用各 phase handler 進(jìn)行處理。
在建立連接過程中,對(duì)于nginx監(jiān)聽到的每個(gè)客戶端連接,都會(huì)將它的讀事件的handler設(shè)置為ngx_http_init_request函數(shù),這個(gè)函數(shù)就是請(qǐng)求處理的入口。在處理請(qǐng)求時(shí),主要就是要解析http請(qǐng)求,比如:uri,請(qǐng)求行等,然后再根據(jù)請(qǐng)求生成響應(yīng)。下面看一下nginx處理的具體過程:
Nginx處理的具體過程
在這里,我們需要了解一下 phase handler 這個(gè)概念。phase 字面的意思,就是階段。所以 phase handlers 也就好理解了,就是包含若干個(gè)處理階段的一些 handler。
在每一個(gè)階段,包含有若干個(gè) handler,再處理到某個(gè)階段的時(shí)候,依次調(diào)用該階段的 handler 對(duì) HTTP Request 進(jìn)行處理。
通常情況下,一個(gè) phase handler 對(duì)這個(gè) request 進(jìn)行處理,并產(chǎn)生一些輸出。通常 phase handler 是與定義在配置文件中的某個(gè) location 相關(guān)聯(lián)的。
一個(gè) phase handler 通常執(zhí)行以下幾項(xiàng)任務(wù):
獲取 location 配置。 產(chǎn)生適當(dāng)?shù)捻憫?yīng)。 發(fā)送 response header。 發(fā)送 response body。
當(dāng) Nginx 讀取到一個(gè) HTTP Request 的 header 的時(shí)候,Nginx 首先查找與這個(gè)請(qǐng)求關(guān)聯(lián)的虛擬主機(jī)的配置。如果找到了這個(gè)虛擬主機(jī)的配置,那么通常情況下,這個(gè) HTTP Request 將會(huì)經(jīng)過以下幾個(gè)階段的處理(phase handlers):
NGX_HTTP_POST_READ_PHASE: 讀取請(qǐng)求內(nèi)容階段 NGX_HTTP_SERVER_REWRITE_PHASE: Server 請(qǐng)求地址重寫階段 NGX_HTTP_FIND_CONFIG_PHASE: 配置查找階段 NGX_HTTP_REWRITE_PHASE: Location請(qǐng)求地址重寫階段 NGX_HTTP_POST_REWRITE_PHASE: 請(qǐng)求地址重寫提交階段 NGX_HTTP_PREACCESS_PHASE: 訪問權(quán)限檢查準(zhǔn)備階段 NGX_HTTP_ACCESS_PHASE: 訪問權(quán)限檢查階段 NGX_HTTP_POST_ACCESS_PHASE: 訪問權(quán)限檢查提交階段 NGX_HTTP_TRY_FILES_PHASE: 配置項(xiàng) try_files 處理階段 NGX_HTTP_CONTENT_PHASE: 內(nèi)容產(chǎn)生階段 NGX_HTTP_LOG_PHASE: 日志模塊處理階段
在內(nèi)容產(chǎn)生階段,為了給一個(gè) request 產(chǎn)生正確的響應(yīng),Nginx 必須把這個(gè) request 交給一個(gè)合適的 content handler 去處理。如果這個(gè) request 對(duì)應(yīng)的 location 在配置文件中被明確指定了一個(gè) content handler,那么Nginx 就可以通過對(duì) location 的匹配,直接找到這個(gè)對(duì)應(yīng)的 handler,并把這個(gè) request 交給這個(gè) content handler 去處理。這樣的配置指令包括像,perl,flv,proxy_pass,mp4等。
如果一個(gè) request 對(duì)應(yīng)的 location 并沒有直接有配置的 content handler,那么 Nginx 依次嘗試:
如果一個(gè) location 里面有配置 random_index on,那么隨機(jī)選擇一個(gè)文件,發(fā)送給客戶端。 如果一個(gè) location 里面有配置 index 指令,那么發(fā)送 index 指令指明的文件,給客戶端。 如果一個(gè) location 里面有配置 autoindex on,那么就發(fā)送請(qǐng)求地址對(duì)應(yīng)的服務(wù)端路徑下的文件列表給客戶端。 如果這個(gè) request 對(duì)應(yīng)的 location 上有設(shè)置 gzip_static on,那么就查找是否有對(duì)應(yīng)的.gz文件存在,有的話,就發(fā)送這個(gè)給客戶端(客戶端支持 gzip 的情況下)。 請(qǐng)求的 URI 如果對(duì)應(yīng)一個(gè)靜態(tài)文件,static module 就發(fā)送靜態(tài)文件的內(nèi)容到客戶端。
內(nèi)容產(chǎn)生階段完成以后,生成的輸出會(huì)被傳遞到 filter 模塊去進(jìn)行處理。filter 模塊也是與 location 相關(guān)的。所有的 filter 模塊都被組織成一條鏈。輸出會(huì)依次穿越所有的 filter,直到有一個(gè) filter 模塊的返回值表明已經(jīng)處理完成。
這里列舉幾個(gè)常見的 filter 模塊,例如:
server-side includes。 XSLT filtering。 圖像縮放之類的。 gzip 壓縮。
在所有的 filter 中,有幾個(gè) filter 模塊需要關(guān)注一下。按照調(diào)用的順序依次說明如下:
copy: 將一些需要復(fù)制的 buf(文件或者內(nèi)存)重新復(fù)制一份然后交給剩余的 body filter 處理。 postpone: 這個(gè) filter 是負(fù)責(zé) subrequest 的,也就是子請(qǐng)求的。 write: 寫輸出到客戶端,實(shí)際上是寫到連接對(duì)應(yīng)的 socket 上。
3.3 請(qǐng)求完整處理過程
根據(jù)以上請(qǐng)求步驟所述,請(qǐng)求完整的處理過程如下圖所示:
請(qǐng)求完整的處理過程
3.4 keepalive 長(zhǎng)連接
當(dāng)然,在nginx中,對(duì)于http1.0與http1.1也是支持長(zhǎng)連接的。
什么是長(zhǎng)連接呢?我們知道,http請(qǐng)求是基于TCP協(xié)議之上的,那么,當(dāng)客戶端在發(fā)起請(qǐng)求前,需要先與服務(wù)端建立TCP連接,而每一次的TCP連接是需要三次握手來確定的,如果客戶端與服務(wù)端之間網(wǎng)絡(luò)差一點(diǎn),這三次交互消費(fèi)的時(shí)間會(huì)比較多,而且三次交互也會(huì)帶來網(wǎng)絡(luò)流量。當(dāng)然,當(dāng)連接斷開后,也會(huì)有四次的交互,當(dāng)然對(duì)用戶體驗(yàn)來說就不重要了。而http請(qǐng)求是請(qǐng)求應(yīng)答式的,如果我們能知道每個(gè)請(qǐng)求頭與響應(yīng)體的長(zhǎng)度,那么我們是可以在一個(gè)連接上面執(zhí)行多個(gè)請(qǐng)求的,這就是所謂的長(zhǎng)連接,但前提條件是我們先得確定請(qǐng)求頭與響應(yīng)體的長(zhǎng)度。
對(duì)于請(qǐng)求來說,如果當(dāng)前請(qǐng)求需要有body,如POST請(qǐng)求,那么nginx就需要客戶端在請(qǐng)求頭中指定content-length來表明body的大小,否則返回400錯(cuò)誤。也就是說,請(qǐng)求體的長(zhǎng)度是確定的,那么響應(yīng)體的長(zhǎng)度呢?先來看看http協(xié)議中關(guān)于響應(yīng)body長(zhǎng)度的確定:
對(duì)于http1.0協(xié)議來說,如果響應(yīng)頭中有content-length頭,則以content-length的長(zhǎng)度就可以知道body的長(zhǎng)度了,客戶端在接收body時(shí),就可以依照這個(gè)長(zhǎng)度來接收數(shù)據(jù),接收完后,就表示這個(gè)請(qǐng)求完成了。而如果沒有content-length頭,則客戶端會(huì)一直接收數(shù)據(jù),直到服務(wù)端主動(dòng)斷開連接,才表示body接收完了。 而對(duì)于http1.1協(xié)議來說,如果響應(yīng)頭中的Transfer-encoding為chunked傳輸,則表示body是流式輸出,body會(huì)被分成多個(gè)塊,每塊的開始會(huì)標(biāo)識(shí)出當(dāng)前塊的長(zhǎng)度,此時(shí),body不需要通過長(zhǎng)度來指定。如果是非chunked傳輸,而且有content-length,則按照content-length來接收數(shù)據(jù)。否則,如果是非chunked,并且沒有content-length,則客戶端接收數(shù)據(jù),直到服務(wù)端主動(dòng)斷開連接。
從上面,我們可以看到,除了http1.0不帶content-length以及http1.1非chunked不帶content-length外,body的長(zhǎng)度是可知的。此時(shí),當(dāng)服務(wù)端在輸出完body之后,會(huì)可以考慮使用長(zhǎng)連接。能否使用長(zhǎng)連接,也是有條件限制的。如果客戶端的請(qǐng)求頭中的connection為close,則表示客戶端需要關(guān)掉長(zhǎng)連接,如果為keep-alive,則客戶端需要打開長(zhǎng)連接,如果客戶端的請(qǐng)求中沒有connection這個(gè)頭,那么根據(jù)協(xié)議,如果是http1.0,則默認(rèn)為close,如果是http1.1,則默認(rèn)為keep-alive。如果結(jié)果為keepalive,那么,nginx在輸出完響應(yīng)體后,會(huì)設(shè)置當(dāng)前連接的keepalive屬性,然后等待客戶端下一次請(qǐng)求。
當(dāng)然,nginx不可能一直等待下去,如果客戶端一直不發(fā)數(shù)據(jù)過來,豈不是一直占用這個(gè)連接?所以當(dāng)nginx設(shè)置了keepalive等待下一次的請(qǐng)求時(shí),同時(shí)也會(huì)設(shè)置一個(gè)大等待時(shí)間,這個(gè)時(shí)間是通過選項(xiàng)keepalive_timeout來配置的,如果配置為0,則表示關(guān)掉keepalive,此時(shí),http版本無論是1.1還是1.0,客戶端的connection不管是close還是keepalive,都會(huì)強(qiáng)制為close。
如果服務(wù)端最后的決定是keepalive打開,那么在響應(yīng)的http頭里面,也會(huì)包含有connection頭域,其值是”Keep-Alive”,否則就是”Close”。如果connection值為close,那么在nginx響應(yīng)完數(shù)據(jù)后,會(huì)主動(dòng)關(guān)掉連接。所以,對(duì)于請(qǐng)求量比較大的nginx來說,關(guān)掉keepalive最后會(huì)產(chǎn)生比較多的time-wait狀態(tài)的socket。一般來說,當(dāng)客戶端的一次訪問,需要多次訪問同一個(gè)server時(shí),打開keepalive的優(yōu)勢(shì)非常大,比如圖片服務(wù)器,通常一個(gè)網(wǎng)頁會(huì)包含很多個(gè)圖片。打開keepalive也會(huì)大量減少time-wait的數(shù)量。
新聞名稱:Nginx常見工作原理及應(yīng)用
鏈接地址:http://newbst.com/news14/99664.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、自適應(yīng)網(wǎng)站、定制網(wǎng)站、網(wǎng)站內(nèi)鏈、網(wǎng)站設(shè)計(jì)公司、網(wǎng)站維護(hù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容