recv() failed (104: connection reset by peer) 問題排查
最近項目中通過Nginx反向代理nodejs(採用nestjs框架)服務,壓測過程中概率性出現502 Bad Gateway
,出現概率較低,大概在0.005%左右,查看日誌具體報錯信息爲recv() failed (104: Connection reset by peer) while reading response header from upstream
,通過查找資料,瞭解到該報錯的直接原因就是:
nodejs服務已經斷開了連接,但是未通知到Nginx,Nginx還在該連接上收發數據,最終導致了該報錯。
考慮到在Nginx中我們配置了打開與nodejs server的長連接,即:
proxy_http_version 1.1;
proxy_set_header Connection "";
我們先從長連接相關的設置入手查找問題,Nginx常用的幾個影響長連接的參數設置有:keepalive_timeout
、keepalive_requests
、keepalive
-
1)
keepalive_timeout
: 設置客戶端的長連接超時時間,如果超過這個時間客戶端沒有發起請求,則Nginx服務器會主動關閉長連接,Nginx默認的keepalive_timeout 75s;
。有些瀏覽器最多隻保持 60 秒,所以我們一般設置爲60s
。如果設置爲0
,則關閉長連接。 -
2)
keepalive_requests
:設置與客戶端的建立的一個長連接可以處理的最大請求次數,如果超過這個值,則Nginx會主動關閉該長連接,默認值爲100
。一般情況下,
keepalive_requests 100
基本上可以滿足需求,但是在 QPS 較高的情況下,不停的有長連接請求數達到最大請求次數而被關閉,這也就意味着Nginx需要不停的創建新的長連接來處理請求,這樣會可能出現大量的TIME WAIT
-
3)
keepalive
: 設置到 upstream 服務器的空閒 keepalive 連接的最大數量,當空閒的 keepalive 的連接數量超過這個值時,最近使用最少的連接將被關閉,如果這個值設置得太小的話,某個時間段請求數較多,而且請求處理時間不穩定的情況,可能就會出現不停的關閉和創建長連接數。我們一般設置1024
即可。特殊場景下,可以通過接口的平均響應時間和QPS估算一下。 -
4)開啓與 upstream 服務器的長連接
默認nginx訪問後端都是用的短連接(HTTP1.0),每次請求來了就新開一個連接,處理完後就關閉該連接,下一次請求再重新開啓。HTTP協議中從1.1版本之後開始對支持長連接。因此我們會在location中設置以下參數來開啓長連接:
http { server { location / { proxy_http_version 1.1; proxy_set_header Connection ""; } } }
1)和 2)是設置客戶端和Nginx之間的長連接,3)和 4)是設置 Nginx 和 server 之間的長連接。
以上參數配置都覈對過沒有問題後,我們開始從node中查找是否有與keepalive相關的配置,通過查找nodejs文檔,發現nodejs中默認的server.keepAliveTimeout
時間爲 5000ms
,這樣如果在 5000s
的超時界限點,Nginx還未收到 Node 端的關閉通知,還繼續在該長連接上收發數據,那麼就可能出現上述的報錯,
因此我們適當加大 node 端的 keepAliveTimeout
值(參考https://shuheikagawa.com/blog/2019/04/25/keep-alive-timeout/):
// Set up the app...
const server = app.listen(8080);
server.keepAliveTimeout = 61 * 1000;
server.headersTimeout = 65 * 1000; // Node.js >= 10.15.2 需要設置該值大於keepAliveTimeout
延長 node 端的 keepAliveTimeout
值的目的就是爲了防止 Nginx 的在 node 服務之前斷開連接,被調用方的超時時間需要大於調用方。
經過上述修改後,壓測時Nginx未出現過502 Bad Gateway
的報錯。