在Nginx中,對於http1.0與http1.1是支持長鏈接的。http請求是基於TCP協議之上的,那麼當客戶端在發起請求前,須要先與服務端創建TCP鏈接,而每一次的TCP鏈接是須要三次握手來肯定的,若是客戶端與服務端之間網絡差一點,這三次交互消費的時間會比較多,並且三次交互也會帶來網絡流量。固然,當鏈接斷開後,也會有四次的交互,固然對用戶體驗來講就不重要了。而http請求是請求應答式的,若是咱們能知道每一個請求頭與響應體的長度,那麼咱們是能夠在一個鏈接上面執行多個請求的,這就是所謂的長鏈接,但前提條件是咱們先得肯定請求頭與響應體的長度。對於請求來講,若是當前請求須要有body,如POST請求,那麼nginx就須要客戶端在請求頭中指定content-length來代表body的大小,不然返回400錯誤。也就是說,請求體的長度是肯定的,那麼響應體的長度呢?先來看看http協議中關於響應body長度的肯定:nginx
1)對於http1.0協議來講,若是響應頭中有content-length頭,則以content-length的長度就能夠知道body的長度了,客戶端在接收body時,就能夠依照這個長度來接收數據,接收完後,就表示這個請求完成了。而若是沒有content-length頭,則客戶端會一直接收數據,直到服務端主動斷開鏈接,才表示body接收完了。
2)對於http1.1協議來講,若是響應頭中的Transfer-encoding爲chunked傳輸,則表示body是流式輸出,body會被分紅多個塊,每塊的開始會標識出當前塊的長度,此時,body不須要經過長度來指定。若是是非chunked傳輸,並且有content-length,則按照content-length來接收數據。不然,若是是非chunked,而且沒有content-length,則客戶端接收數據,直到服務端主動斷開鏈接。ajax
從上面,咱們能夠看到,除了http1.0不帶content-length以及http1.1非chunked不帶content-length外,body的長度是可知的。此時,當服務端在輸出完body以後,會能夠考慮使用長鏈接。可否使用長鏈接,也是有條件限制的。若是客戶端的請求頭中的connection爲close,則表示客戶端須要關掉長鏈接,若是爲keep-alive,則客戶端須要打開長鏈接,若是客戶端的請求中沒有connection這個頭,那麼根據協議,若是是http1.0,則默認爲close,若是是http1.1,則默認爲keep-alive。若是結果爲keepalive,那麼,nginx在輸出完響應體後,會設置當前鏈接的keepalive屬性,而後等待客戶端下一次請求。固然,nginx不可能一直等待下去,若是客戶端一直不發數據過來,豈不是一直佔用這個鏈接?因此當nginx設置了keepalive等待下一次的請求時,同時也會設置一個最大等待時間,這個時間是經過選項keepalive_timeout來配置的,若是配置爲0,則表示關掉keepalive,此時,http版本不管是1.1仍是1.0,客戶端的connection無論是close仍是keepalive,都會強制爲close。後端
若是服務端最後的決定是keepalive打開,那麼在響應的http頭裏面,也會包含有connection頭域,其值是」Keep-Alive」,不然就是」Close」。若是connection值爲close,那麼在nginx響應完數據後,會主動關掉鏈接。因此,對於請求量比較大的nginx來講,關掉keepalive最後會產生比較多的time-wait狀態的socket。通常來講,當客戶端的一次訪問,須要屢次訪問同一個server時,打開keepalive的優點很是大,好比圖片服務器,一般一個網頁會包含不少個圖片,打開keepalive會大量減小time-wait數量。緩存
先說說服務爲何使用HTTPs長鏈接技術?好比下面幾個緣由:
1)對響應時間要求較高;
2)服務走的是公網,客戶端與服務端的TCP創建的三次握手和斷開的四次揮手都須要40ms左右(真實數據包計算出來的),共須要80ms左右;
3)每一個接入方使用的IP就若干個,須要創建的請求鏈接有限。
4)使用長鏈接技術,能夠大幅減小TCP頻繁握手的次數,極大提升響應時間;同時,即便使用長鏈接技術,也不須要消耗不少的系統資源用來緩存sockets會話信息。服務器
Nginx反向代理時保持長鏈接
網絡
1)場景描述
HTTP1.1以後,HTTP協議支持持久鏈接,也就是長鏈接,優勢在於在一個TCP鏈接上能夠傳送多個HTTP請求和響應,減小了創建和關閉鏈接的消耗和延遲。若是咱們使用了nginx去做爲反向代理或者負載均衡,從客戶端過來的長鏈接請求就會被轉換成短鏈接發送給服務器端。爲了支持長鏈接,咱們須要在nginx服務器上作一些配置。負載均衡
2)要求
當使用 Nginx做爲反向代理時,爲了支持長鏈接,須要作到兩點::
- 從Client到Nginx的鏈接是長鏈接。
- 從Nginx到Server的鏈接是長鏈接。socket
從 HTTP 協議的角度看,Nginx在這個過程當中,對於客戶端它扮演着 HTTP 服務器端的角色。而對於真正的服務器端(在 Nginx的術語中稱爲 upstream)Nginx又扮演着 HTTP 客戶端的角色。也就是說對於Client客戶端而言,Nginx其實扮演着Server的角色,反之,之於Server,Nginx就是一個Client。ide
3)保持和 Client 的長鏈接
要想作到Client與Nginx之間保持長鏈接,須要:
- Client發送過來的HTTP請求要求攜帶"keep-alive"header。
- Nginx設置支持keepalive性能
[Nginx中HTTP配置]
默認狀況下,Nginx已經自動開啓了對 Client 鏈接的 keepalive 支持。通常場景能夠直接使用,可是對於一些比較特殊的場景,仍是有必要調整個別參數。須要修改 Nginx的配置文件 (在 Nginx安裝目錄下的conf/nginx.conf):
http { keepalive_timeout 120s; #客戶端連接超時時間。爲0的時候禁用長鏈接。即長鏈接的timeout keepalive_requests 10000; #在一個長鏈接上能夠服務的最大請求數目。當達到最大請求數目且全部已有請求結束後,鏈接被關閉。默認值爲100。即每一個鏈接的最大請求數 }
大多數狀況下,keepalive_requests = 100也夠用,可是對於 QPS 較高的場景,很是有必要加大這個參數,以免出現大量鏈接被生成再拋棄的狀況,減小TIME_WAIT。QPS=10000 時,客戶端每秒發送 10000 個請求 (一般創建有多個長鏈接),每一個鏈接只能最多跑 100 次請求,意味着平均每秒鐘就會有 100 個長鏈接所以被 nginx 關閉。一樣意味着爲了保持 QPS,客戶端不得不每秒中從新新建 100 個鏈接。所以,若是用netstat命令看客戶端機器,就會發現有大量的TIME_WAIT的socket鏈接 (即便此時keep alive已經在 Client 和 NGINX 之間生效)。
keepalive_timeout 指令,語法以下:
Syntax: keepalive_timeout timeout [header_timeout]; Default: keepalive_timeout 75s; Context: http, server, location
第一個參數設置 keepalive 客戶端鏈接在服務器端保持開啓的超時值。值爲 0 會禁用 keepalive客戶端鏈接。
可選的第二個參數在響應的 header 域中設置一個值 "Keep-Alive: timeout=time"。這兩個參數能夠不同。
須要注意:默認 75s 通常狀況下也夠用,對於一些請求比較大的內部服務器通信的場景,適當加大爲 120s 或者 300s。第二個參數一般能夠不用設置。
keepalive_requests 指令
keepalive_requests 指令用於設置一個keepalive鏈接上能夠服務的請求的最大數量。當最大請求數量達到時,鏈接被關閉。默認是100。
這個參數的真實含義,是指一個keepalive創建以後,Nginx就會爲這個鏈接設置一個計數器,記錄這個keepalive的長鏈接上已經接收並處理的客戶端請求的數量。若是達到這個參數設置的最大值時,則 Nginx會強行關閉這個長鏈接,逼迫客戶端不得不從新創建新的長鏈接。
這個參數每每被大多數人忽略,由於大多數狀況下當 QPS(每秒請求數) 不是很高時,默認值 100 湊合夠用。可是,對於一些 QPS 比較高(好比超過 10000QPS,甚至達到 30000,50000 甚至更高) 的場景,默認的 100 就顯得過低。
簡單計算一下,QPS=10000 時,客戶端每秒發送 10000 個請求 (一般創建有多個長鏈接),每一個鏈接只能最多跑 100 次請求,意味着平均每秒鐘就會有 100 個長鏈接所以被 Nginx關閉。一樣意味着爲了保持 QPS,客戶端不得不每秒中從新新建 100 個鏈接。所以,若是用netstat命令看客戶端機器,就會發現有大量的TIME_WAIT的socket鏈接 (即便此時keep alive已經在 Client 和 Nginx之間生效)。所以對於 QPS 較高的場景,很是有必要加大keepalive_requests這個參數,以免出現大量鏈接被生成再拋棄的狀況,減小TIME_WAIT。
4)保持和Server的長鏈接
想讓Nginx和Server之間維持長鏈接,最樸素最典型的設置以下:
http { upstream backend { server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s; server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s; keepalive 300; #這個很重要! } server { listen 8080 default_server; server_name ""; location / { proxy_pass http://backend; proxy_http_version 1.1; #設置http版本爲1.1 proxy_set_header Connection ""; #設置Connection爲長鏈接(默認爲no) } } }
[upstream配置]
在upstream配置中,有一個參數特別重要特別當心,就是keepalive。大多數未仔細研讀過 Nginx的同窗一般都會誤解這個參數,有些人理解爲這裏的 keepalive 是設置是否打開長鏈接,覺得應該設置爲 ON/OFF。有些人會被前面的 keepalive_timeout 誤導,覺得這裏也是設置 keepalive 的 timeout。可是實際上這個keepalive參數的含義很是的奇特:
Syntax: keepalive connections; Default: — Context: upstream
Activates the cache for connections to upstream servers.
激活到 upstream 服務器的鏈接緩存。
The connections parameter sets the maximum number of idle keepalive connections to upstream servers that are preserved in the cache of each worker process. When this number is exceeded, the least recently used connections are closed.
connections 參數設置每一個 worker 進程在緩衝中保持的到 upstream 服務器的空閒 keepalive 鏈接的最大數量. 當這個數量被突破時,最近使用最少的鏈接將被關閉。
It should be particularly noted that the keepalive directive does not limit the total number of connections to upstream servers that an nginx worker process can open. The connections parameter should be set to a number small enough to let upstream servers process new incoming connections as well.
keepalive 指令不會限制一個 nginx worker 進程到 upstream 服務器鏈接的總數量。connections 參數應該設置爲一個足夠小的數字來讓 upstream 服務器來處理新進來的鏈接。
在這裏能夠看到,前面的幾種猜想能夠確認是錯誤的了:
- keepalive 不是 ON/OFF 之類的開關
- keepalive 不是 TIMEOUT,不是用來設置超時值
不少人讀到這裏後,會產生另一個誤解:認爲這個參數是設置到upstream服務器的長鏈接的數量,分歧在因而最大鏈接數仍是最小鏈接數,不得不說這也是一個挺逗的分歧……
這裏請特別注意Nginx文檔裏的這句話,相當重要:
The connections parameter sets the maximum number of idle keepalive connections to upstream servers connections 參數設置到 upstream 服務器的空閒 keepalive 鏈接的最大數量
請仔細體會這個 idle 的概念,何爲idle。大多數人之因此誤解爲是到upstream服務器的最大長鏈接數,通常都是由於看到了文檔中的這句話,而漏看了這個idle一詞。
而後繼續看Nginx文檔後面另一句話:
When this number is exceeded, the least recently used connections are closed. 當這個數量被突破時,最近使用最少的鏈接將被關閉。
這句話更是大大強化了前面關於keepalive設置的是最大長鏈接數的誤解:若是鏈接數超過keepalive的限制,就關閉鏈接。這不是赤裸裸的最大鏈接數麼?可是 Nginx的文檔立馬給出了指示,否認了最大鏈接數的可能:
It should be particularly noted that the keepalive directive does not limit the total number of connections to upstream servers that an nginx worker process can open. 特別提醒:keepalive 指令不會限制一個 nginx worker 進程到 upstream 服務器鏈接的總數量。
keepalive 參數的理解
要真正理解 keepalive 參數的含義,請回到Nginx文檔中的這句:
The connections parameter sets the maximum number of idle keepalive connections to upstream servers connections 參數設置到 upstream 服務器的空閒 keepalive 鏈接的最大數量
請注意空閒 keepalive 鏈接的最大數量中空閒這個關鍵字。keepalive這個參數和以前http裏面的 keepalive_timeout 不同。這個參數的含義是,鏈接池裏面最大的空閒鏈接數量。不理解?不要緊,下面來舉個例子:
先假設一個場景: 有一個 HTTP 服務,做爲upstream服務器接收請求,響應時間爲 100 毫秒。若是要達到 10000 QPS 的性能,就須要在 nginx 和upstream服務器之間創建大約 1000 條 HTTP 鏈接。nginx 爲此創建鏈接池,而後請求過來時爲每一個請求分配一個鏈接,請求結束時回收鏈接放入鏈接池中,鏈接的狀態也就更改成 idle。
再假設這個upstream服務器的keepalive參數設置比較小,好比常見的 10.
假設請求和響應是均勻而平穩的,那麼這 1000 條鏈接應該都是一放回鏈接池就當即被後續請求申請使用,線程池中的 idle 線程會很是的少,趨進於零。咱們以 10 毫秒爲一個單位,來看鏈接的狀況 (注意場景是 1000 個線程 + 100 毫秒響應時間,每秒有 10000 個請求完成):
1)每 10 毫秒有 100 個新請求,須要 100 個鏈接
2)每 10 毫秒有 100 個請求結束,能夠釋放 100 個鏈接
3)若是請求和應答都均勻,則 10 毫秒內釋放的鏈接恰好夠用,不須要新建鏈接,鏈接池空閒鏈接爲零
而後再回到現實世界,請求一般不是足夠的均勻和平穩,爲了簡化問題,咱們假設應答始終都是平穩的,只是請求不平穩,第一個 10 毫秒只有 50, 第二個 10 毫秒有 150:
1)下一個 10 毫秒,有 100 個鏈接結束請求回收鏈接到鏈接池,可是假設此時請求不均勻 10 毫秒內沒有預計的 100 個請求進來,而是隻有 50 個請求。注意此時鏈接池回收了 100 個鏈接又分配出去 50 個鏈接,所以鏈接池內有 50 個空閒鏈接。
2)而後注意看keepalive=10的設置,這意味着鏈接池中最多允許保留有 10 個空閒鏈接。所以 nginx 不得不將這 50 個空閒鏈接中的 40 個關閉,只留下 10 個。
3)再下一個 10 個毫秒,有 150 個請求進來,有 100 個請求結束任務釋放鏈接。150 - 100 = 50, 空缺了 50 個鏈接,減掉前面鏈接池保留的 10 個空閒鏈接,nginx 不得不新建 40 個新鏈接來知足要求。
能夠看到,在短短的 20 毫秒內,僅僅由於請求不夠均勻,就致使 nginx 在前 10 毫秒判斷空閒鏈接過多關閉了 40 個鏈接,然後 10 毫秒又不得不新建 40 個鏈接來彌補鏈接的不足。
再來一次相似的場景,假設請求是均勻的,而應答再也不均勻,前 10 毫秒只有 50 個請求結束,後 10 毫秒有 150 個:
1)前 10 毫秒,進來 100 個請求,結束 50 個請求,致使鏈接不夠用,nginx 爲此新建 50 個鏈接
2)後 10 毫秒,進來 100 個請求,結束 150 個請求,致使空閒鏈接過多,ngixn 爲此關閉了150-100-10=40個空閒鏈接
特別提醒:第二個應答不均勻的場景其實是對應第一個請求不均勻的場景:正是由於請求不均勻,因此致使 100 毫秒以後這些請求的應答必然不均勻。
現實世界中的請求每每和理想狀態有巨大差別,請求不均勻,服務器處理請求的時間也不平穩,這理論上的大概 1000 個鏈接在反覆的回收和再分配的過程當中,必然出現兩種很是矛盾場景在短期內反覆:
1)鏈接不夠用,形成新建鏈接。
2)鏈接空閒,形成關閉鏈接。從而使得總鏈接數出現反覆震盪,不斷的建立新鏈接和關閉鏈接,使得長鏈接的效果被大大削弱。
形成鏈接數量反覆震盪的一個推手,就是這個keepalive 這個最大空閒鏈接數。畢竟鏈接池中的 1000 個鏈接在頻繁利用時,出現短期內多餘 10 個空閒鏈接的機率實在過高。上面狀況說的都是keepalive設置不合理致使Nginx有屢次釋放與建立鏈接的過程,形成資源浪費。所以爲了不出現上面的鏈接震盪,必須考慮加大這個參數,好比上面的場景若是將keepalive設置爲100或者200, 就能夠很是有效的緩衝請求和應答不均勻。
總結:keepalive 這個參數必定要當心設置,尤爲對於 QPS 比較高的場景,推薦先作一下估算,根據 QPS 和平均響應時間大致能計算出須要的長鏈接的數量。好比前面 10000 QPS 和 100 毫秒響應時間就能夠推算出須要的長鏈接數量大概是 1000,而後將keepalive設置爲這個長鏈接數量的 10% 到 30%。比較懶的同窗,能夠直接設置爲 keepalive=1000 之類的,通常都 OK 的了。
[location配置]
http { server { location / { proxy_pass http://backend; proxy_http_version 1.1; #設置http版本爲1.1 proxy_set_header Connection ""; #設置Connection爲長鏈接(默認爲no) } } }
HTTP 協議中對長鏈接的支持是從 1.1 版本以後纔有的,所以最好經過 proxy_http_version 指令設置爲 1.1。HTTP1.0不支持keepalive特性,當沒有使用HTTP1.1的時候,後端服務會返回101錯誤,而後斷開鏈接。而 "Connection" header 能夠選擇被清理,這樣即使是 Client 和 Nginx 之間是短鏈接,Nginx 和 upstream 之間也是能夠開啓長鏈接的。這種狀況下必須清理來自 Client 請求中的 「Connection」 header。
[另一種高級方式]
http { map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream backend { server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s; server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s; keepalive 300; } server { listen 8080 default_server; server_name ""; location / { proxy_pass http://backend; proxy_connect_timeout 15; #與upstream server的鏈接超時時間(沒有單位,最大不能夠超過75s) proxy_read_timeout 60s; #nginx會等待多長時間來得到請求的響應 proxy_send_timeout 12s; #發送請求給upstream服務器的超時時間 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } } }
http裏面的map的做用是:
- 讓轉發到代理服務器的 "Connection" 頭字段的值,取決於客戶端請求頭的 "Upgrade" 字段值。
- 若是 $http_upgrade沒有匹配,那 "Connection" 頭字段的值會是upgrade。
- 若是 $http_upgrade爲空字符串的話,那 "Connection" 頭字段的值會是 close。
[補充]
Nginx支持WebSocket。對於Nginx將升級請求從客戶端發送到後臺服務器,必須明確設置Upgrade和Connection標題。這也算是上面狀況所很是用的場景。HTTP的Upgrade協議頭機制用於將鏈接從HTTP鏈接升級到WebSocket鏈接,Upgrade機制使用了Upgrade協議頭和Connection協議頭。爲了讓Nginx能夠未來自客戶端的Upgrade請求發送到後端服務器,Upgrade和Connection的頭信息必須被顯式的設置。
[注意]
在Nginx的配置文件中,若是當前模塊中沒有proxy_set_header的設置,則會從上級別繼承配置。繼承順序爲:http, server, location。若是在下一層使用proxy_set_header修改了header的值,則全部的header值均可能會發生變化,以前繼承的全部配置將會被丟棄。因此,儘可能在同一個地方進行proxy_set_header,不然可能會有別的問題。
Nginx配置長鏈接 (ajax60秒請求超時)
在使用ajax作輪訓的時候前臺發出的ajax請求老是會在60秒以後返回405超時響應,通過排除ajax超時響應設置後猜想nginx對請求進行了超時響應處理,猜想是nginx配置有問題;Nginx從 1.1.4 開始,實現了對後端機器的長鏈接支持,這意味着 Nginx 與後端機器的通訊效率更高,後端機器的負擔更低。
server { listen 80; server_name www.kevin.com; location / { proxy_http_version 1.1; proxy_read_timeout 600s; #新增配置1 proxy_send_timeout 120s; #新增配置2 proxy_pass http://127.0.0.1:8086; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
proxy_read_timeout: 鏈接成功後_等候後端服務器響應時間_其實已經進入後端的排隊之中等候處理(也能夠說是後端服務器處理請求的時間)proxy_send_timeout: 後端服務器數據回傳時間, 就是在規定時間以內後端服務器必須傳完全部的數據。