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