Nginx學習:HTTP核心模塊(五)長連接與連接處理
HTTP 基礎知識大家掌握的怎么樣呀?對于長連接這一塊的內(nèi)容應該也不是什么新鮮東西了吧。畢竟 HTTP1.1 都已經(jīng)發(fā)布這么久了。今天主要來看的就是長連接相關的配置,另外還會介紹幾個和連接有關的內(nèi)容。同時,今天的內(nèi)容除了 HTTP 外,還需要一點 TCP 的知識。沒辦法,畢竟 Ngxin 本身就是一個網(wǎng)絡代理服務器軟件,離不開的就是各種網(wǎng)絡相關的知識。相信大家肯定沒問題的,畢竟很早前在短視頻中就說過,基礎能比我差的程序員估計在全國范圍內(nèi)都找不到幾個了。
但是不要臉和硬著頭皮啃的水平咱還是有的,因此,如果有遺漏或錯誤的地方,也懇請各位大佬在評論區(qū)指出。
好了,不多廢話了,進入主題吧。
長連接
關于長連接的知識,屬于 HTTP 的基礎知識了,咱們不多說,不了解的小伙伴可以去查閱下相關資料。簡單來說,一次 HTTP 連接,就要經(jīng)歷 TCP 的三次握手四次揮手,畢竟它是處于網(wǎng)絡的第七層,同時也是基于第四層的 TCP 來實現(xiàn)的。如果請求很多,需要不停地建立 TCP 連接,效率明顯下降。而長連接則是一次連接后保持這個連接一段時間,如果有其它的請求就可以復用這條連接,從而減少網(wǎng)絡連接開銷,提升效率。比如很多門戶或者電商站,一打開就是一大堆的 JS、圖片之類的請求。如果每一個都要單獨建立連接,勢必會影響頁面的整體打開速度。
同時,在進行反向代理的時候,也可以啟用長連接功能,減少后端代理的連接次數(shù)。反向代理相關的配置我們在后面學習反向代理相關的內(nèi)容時再說,現(xiàn)在學習的主要是針對 http、server、location 模塊的長連接配置。
在 Nginx 中,有完整的長連接配置。
keepalive_disable
指定哪些瀏覽器不使用長連接功能,或者說是針對行為異常的瀏覽器關閉長連接功能。
keepalive_disable?none?|?browser?...;
默認 msie6 , 值為 msie6 表示在遇到POST請求時,關閉與老版本 MSIE 瀏覽器建立長連接。 值為 safari 表示在遇到 Mac OS X 和類 Mac OS X 操作系統(tǒng)下的 Safari 瀏覽器和類 Safari 瀏覽器時,不與瀏覽器建立長連接。 值為none表示為所有瀏覽器開啟長連接功能。
在 nginx 1.1.18 版本及以前,safari 將匹配所有操作系統(tǒng)上的 Safari 和類 Safari 瀏覽器,并默認不與這些瀏覽器建立長連接。
我本地是 Mac 電腦,因此,直接設置一個 safari 的配置。
http?{
??……
??keepalive_disable?safari;
?……
}
配置完成后,使用 Safari 和 Chrome 分別測試,可以看到 Safari 的 Response Header 中的 Connection 的值變成了 close ,而 Chrome 還是正常的 keep-alive 。
keepalive_requests
設置通過一個長連接可以處理的最大請求數(shù)。 請求數(shù)超過此值,長連接將關閉。
keepalive_requests?number;
在版本1.19.10之前,默認值為100,現(xiàn)在默認值是 1000 。定期關閉連接對于釋放每個連接的內(nèi)存分配是必要的。因此,使用過高的最大請求數(shù)可能會導致內(nèi)存使用過多,因此不建議使用。
我們直接配置一個,將它的 number 設置為 2 ,然后建立一個 html 頁面以及一堆空的 js 文件。
//?nginx.conf
http?{
??……
??keepalive_requests?2;
?……
}
//?testkeepalive1.html
~
~
"testkeepalive1.html"?23L,?461C??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????20,19-26??????All
<!DOCTYPE?html>
<html>
<head>
<title>Welcome?to?nginx!</title>
<style>
html?{?color-scheme:?light?dark;?}
body?{?width:?35em;?margin:?0?auto;
font-family:?Tahoma,?Verdana,?Arial,?sans-serif;?}
</style>
</head>
<body>
<h1>Welcome?to?nginx!</h1>
This?is?testkeepalive1.html
<script?src="js1/1.js"></script>
<script?src="js1/2.js"></script>
<script?src="js1/3.js"></script>
<script?src="js1/4.js"></script>
<script?src="js1/5.js"></script>
</body>
</html>
好了,現(xiàn)在訪問 http://192.168.56.88/testkeepalive1.html 頁面,查看所有的請求,目前我們算上這個 html 頁面,一共會有 6 個請求。你會發(fā)現(xiàn)有的請求的 Connection 會變成 Close ,也就是說一條長連接在請求數(shù)量達到設置的值之后就斷了,然后又是一個新的請求連接。
一般情況下不需要刻意設置這個值,上面文檔的說明中也說過了使用過高的最大請求數(shù)可能會導致內(nèi)存使用過多,正常來說 1000 已經(jīng)相當夠用了,即使是淘寶這樣的網(wǎng)站,首頁上的請求數(shù)也沒多少,畢竟大家還會合并請求及圖片來進行連接的優(yōu)化。
keepalive_time
限制通過一個保持活動連接處理請求的最長時間。
keepalive_time?time;
達到此時間后,連接在后續(xù)請求處理后關閉,默認值是 1h 。它是 Nginx 1.19.10 之后新出的配置,咱們就不做詳細的測試了,如果想要測試的同學,可以根據(jù)下面的 keepalive_timeout 測試,然后訪問一個動態(tài)能夠 sleep 的請求路徑,讓 sleep 時間超過這個配置的時間,接著使用一個靜態(tài)頁面來定時發(fā)送 ajax 請求查看連接復用情況。比較麻煩,日常估計使用的小伙伴也不會很多,它默認的 1h 估計很多耗時連接也不可能去做這么長時間的操作。
keepalive_timeout
設置客戶端的長連接在服務器端保持的最長時間
keepalive_timeout?timeout?[header_timeout];
在 timeout 設置的時間內(nèi),客戶端未發(fā)起新請求,則長連接關閉。第二個參數(shù)為可選項,設置 “Keep-Alive: timeout=time” 響應頭的值,可以為這兩個參數(shù)設置不同的值?!癒eep-Alive: timeout=time” 響應頭可以被 Mozilla 和 Konqueror 瀏覽器識別和處理,MSIE 瀏覽器在大約 60 秒后會關閉長連接。
默認安裝完成之后的 Nignx 配置中,這個選項被設置為 65 。如果發(fā)現(xiàn) Nginx 占用服務器的 CPU 特別高,可以嘗試調(diào)低這個時間,或者直接設置成 0 ,設置成 0 后將整個關閉 keepalive 長連接的功能。
現(xiàn)在我們一起來進行測試,首先添加如下配置。
http?{
??……
??keepalive_timeout??0;
?……
}
重載配置后使用一個 Linux 命令查看連接情況。
[root@localhost?html]#?netstat?-nat|grep?-i?"80"
tcp????????0??????0?0.0.0.0:8080????????????0.0.0.0:*???????????????LISTEN
tcp????????0??????0?0.0.0.0:80??????????????0.0.0.0:*???????????????LISTEN
看來目前還沒有連接過來,我們可以訪問之前的 /testkeepalive1.html ,馬上就能看到有新的連接建立了。
[root@localhost?html]#?netstat?-nat|grep?-i?"80"
tcp????????0??????0?0.0.0.0:8080????????????0.0.0.0:*???????????????LISTEN
tcp????????0??????0?0.0.0.0:80??????????????0.0.0.0:*???????????????LISTEN
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:49816??????TIME_WAIT
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:49811??????TIME_WAIT
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:49810??????TIME_WAIT
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:49814??????TIME_WAIT
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:49805??????TIME_WAIT
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:49812??????TIME_WAIT
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:49813??????TIME_WAIT
因為我們將 keepalive_timeout 設置成 0 了,所以現(xiàn)在有 7 條連接建立成功,注意,我使用的是 Chrome ,所以還會自帶一個 /favicon.ico 請求,加上前面的 html 以及 5 個 js ,正好 7 個連接。如果速度快點過來查看的話,可以看到連接是 ESTABLISHED 狀態(tài),表示連接正在使用,TIME_WAIT 表示連接在等待中。
接下來,我們修改 keepalive_timeout 為 5 ,意思就是 5 秒,如果沒有使用,就會用新的連接?,F(xiàn)在再次訪問,會看到依然建立了 5 個連接,畢竟我們相當于同時請求本地的虛擬機,速度還是夠快的,只有兩個連接被復用了,其它的還是建立了連接。另外,默認 Chrome 可以同時建立 6 個連接,你可以測試再多加幾個 js ,然后試試,最多它就只能建立 6 個連接,前面的 js 沒有加載完,后面的就會等待加載,這里和長連接無關,即使不使用長連接,也會在 6 個連接中的某一個連接釋放之后才會建立新的連接。
[root@localhost?html]#?netstat?-nat|grep?-i?"80"
tcp????????0??????0?0.0.0.0:8080????????????0.0.0.0:*???????????????LISTEN
tcp????????0??????0?0.0.0.0:80??????????????0.0.0.0:*???????????????LISTEN
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52469??????ESTABLISHED
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52471??????ESTABLISHED
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52470??????ESTABLISHED
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52472??????ESTABLISHED
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52465??????ESTABLISHED
在 5 秒內(nèi),刷新頁面,不會有新的連接出現(xiàn),但是等待 5 秒后,再次刷新,就會發(fā)現(xiàn)又有新的連接建立了。
[root@localhost?html]#?netstat?-nat|grep?-i?"80"
tcp????????0??????0?0.0.0.0:8080????????????0.0.0.0:*???????????????LISTEN
tcp????????0??????0?0.0.0.0:80??????????????0.0.0.0:*???????????????LISTEN
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52469??????TIME_WAIT
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52471??????TIME_WAIT
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52692??????ESTABLISHED
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52691??????ESTABLISHED
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52690??????ESTABLISHED
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52686??????ESTABLISHED
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52693??????ESTABLISHED
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52470??????TIME_WAIT
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52472??????TIME_WAIT
tcp????????0??????0?192.168.56.88:80????????192.168.56.1:52465??????TIME_WAIT
再等待一段時間后,所有連接全部關閉。這里關閉連接的時間是操作系統(tǒng)決定的,與操作系統(tǒng)被動斷開 TCP 連接的配置有關,主要是 net.ipv4.tcp_tw_reuse 這個配置,大家可以自己查閱相關的資料。正常情況下是在 TIME_WAIT 狀態(tài)后等待大約 2 分鐘,最低可以修改為 1 分鐘,通過下面的命令。這個是操作系統(tǒng)的固定值,最低只能是 60 秒,也就是這個 1 分鐘。
[root@localhost?html]#?sysctl?net.ipv4.tcp_tw_reuse=1
[root@localhost?html]#?sysctl?-p
這樣一分鐘以后這些連接信息就會消失了。設置成 0 不管用的,一樣會等待,客戶端關掉瀏覽器也沒用。這個命令真實的作用其實是如果連接是安全可控的,可以復用 TIME_WAIT 的連接為新的連接所用。它可以快速復用處于 TIME_WAIT 狀態(tài)的 TCP 連接,也就相當于縮短 TIME_WAIT 狀態(tài)的持續(xù)時間。只有客戶端主動關閉連接才會讓服務器的正常關閉,使用 curl 命令測試就可以看到效果,你不會看到任何等待的連接。
關于 TIME_WAIT ,其實是為了能夠正確、自然地進行 TCP 四次揮手而預留的等待時間,更具體的內(nèi)容,大家可以再自行查閱 WSL(最大報文生產(chǎn)周期) 相關的知識點。同時,net.ipv4.tcp_tw_reuse 也要慎用,當客戶端與服務端主機時間不同步時,客戶端的發(fā)送的消息有可能會被直接拒絕掉。正式環(huán)境使用時,建議:別動!保持默認就好。
長連接總結
關于長連接的內(nèi)容寫了這么多,但其實也僅僅只是四個配置指令而已。更重要的其實是對于 HTTP 基礎知識的學習,長連接現(xiàn)在在 Nginx 中是默認打開的,這幾個選項也都是有默認值配置好的。所以平常其實我們不太需要去關心他們的配置。就像上面說的,如果發(fā)現(xiàn) CPU 莫名升高,而且是因為 Nginx 的話,那么可以適當調(diào)節(jié)部分參數(shù)。但 CPU 的問題也不一定僅僅是連接的問題,gzip 同樣也會帶來 CPU 的壓力。因此,調(diào)優(yōu)是一個綜合的活,要找到問題所在才好應對,比如我們還可以查看連接中 TIME_WAIT 的情況來看是不是連接非常多,這時更好的方案其實是要做負載均衡分散壓力了。
連接處理
連接處理主要是針對 Nginx 如何來關閉客戶端連接的一些配置操作。Nginx 在接收客戶端的請求時可能由于客戶端或服務端出錯了,要立即響應錯誤信息給客戶端,而 Nginx 在響應錯誤信息后大分部情況下是需要關閉當前連接的。Nginx 執(zhí)行完 write() 系統(tǒng)調(diào)用(操作系統(tǒng)函數(shù),參考網(wǎng)絡編程或操作系統(tǒng)相關資料)把錯誤信息發(fā)送給客戶端,write() 系統(tǒng)調(diào)用返回成功并不表示數(shù)據(jù)已經(jīng)發(fā)送到客戶端,有可能還在 tcp 連接的 write buffer 里。所以當在某些場景下出現(xiàn) tcp write buffer 里的數(shù)據(jù)在 write() 系統(tǒng)調(diào)用之后到 close() 系統(tǒng)調(diào)用執(zhí)行之前沒有發(fā)送完畢,且 tcp read buffer 里面還有數(shù)據(jù)沒有讀,close() 系統(tǒng)調(diào)用會導致客戶端收到 RST 報文且不會拿到服務端發(fā)送過來的錯誤信息數(shù)據(jù)。
所以,解決問題的重點是,讓服務端別發(fā) RST 包?;蛘哒f延遲發(fā)送,這就是下面要講的 lingering_close 所要解決的問題。
上面的概念看著就很暈吧,如何測試我也沒找到相關的資料,自己也嘗試了半天沒有什么效果。所以對這一塊有了解的同學可以評論區(qū)留言哦。在實際應用中,是否應該打開 lingering_close 呢?這個就沒有固定的推薦值了,lingering_close 的主要作用是保持更好的客戶端兼容性,但是卻需要消耗更多的額外資源(比如連接會一直占著)。因此,秉承對于不懂的東西,默認的就是最好的原則,咱們保持默認狀態(tài)就好了。將來如果學習或者接觸到這一塊的內(nèi)容了,再寫文章和錄視頻進行詳細的學習吧。
lingering_close
控制 Nginx 如何關閉客戶端連接。
lingering_close?off?|?on?|?always;
它的默認值是 on ,指示 Nginx 在完成關閉連接前等待和處理客戶端發(fā)來的額外數(shù)據(jù)。但只有在預測客戶端可能發(fā)送更多數(shù)據(jù)的情況才會做此處理。為了控制關閉HTTP/2連接,必須在 server 下(1.19.1)指定該指令。
always 指示 Nginx 無條件等待和處理客戶端的額外數(shù)據(jù)。
off 指示nginx立即關閉連接,而絕不等待客戶端傳送額外數(shù)據(jù)。 這樣做破壞了協(xié)議,所以正常條件下不應使用。
lingering_time
lingering_close 生效時(非off),這條指令定義 Nginx 處理(讀取但忽略)客戶端額外數(shù)據(jù)的最長時間。
lingering_time?time;
默認值是 30s,超過這段時間后,Nginx 將關閉連接,不論是否還有更多數(shù)據(jù)待處理。
lingering_timeout
lingering_close 生效時(非off),這條指令定義 Nginx 等待客戶端更多數(shù)據(jù)到來的最長時間。
lingering_timeout?time;
默認值是 5s ,如果在這段時間內(nèi),Nginx 沒有接收到數(shù)據(jù),Nginx 將關閉連接。否則,Nginx 將接收數(shù)據(jù),忽略它,然后再等待更多數(shù)據(jù)。 這個“等待——接收——忽略”的循環(huán)一直重復,但總時間不會超過 lingering_time 指令定義的時間。
總結
怎么說呢,學這些真的想回去好好再補補網(wǎng)絡知識了。就這么點內(nèi)容,也已經(jīng)是邊查資料邊測試邊寫了,后面還有那么多配置以及牽涉到的相關知識,想想都頭大。不過沒關系,誰讓自己喜歡這行呢,各位同學是不是也感覺到每次看完文章或者視頻也會跟我一樣多少會有一點點的進步呢?
長連接一般我們都會簡單配置一下,通常也是以 keepalive_time 和 keepalive_time_out 的配置為主,其它兩個說實話,沒學之前我都不知道有這倆貨。另外一個連接處理相關的配置更是從來沒用過。但是,現(xiàn)在起碼我們了個印象,將來或許哪天它們就能為我們解決大問題呢。
參考文檔:文章來源:http://www.zghlxwxcb.cn/news/detail-579020.html
http://nginx.org/en/docs/http/ngx_http_core_module.html文章來源地址http://www.zghlxwxcb.cn/news/detail-579020.html
到了這里,關于【Nginx08】Nginx學習:HTTP核心模塊(五)長連接與連接處理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!