之前踩過這個坑,在《使用 nginx 作反向代理,啟用 keepalive 時,遇到 502 錯誤的調查過程》 中了記錄調查過程,當時多個案例同時查,記錄的比較亂,這里重新整理一下結論。
ingress-nginx 到 upstream 的長連接通過configmap中的 upstream-keepalive-connections 等參數設置,注意與 keep-alive 區分(見文末)。另外 ingress-nginx 0.20 之前的版本有 bug,即使配置了也不生效:ingress-nginx upstream 的 keep-alive 不生效。
1.三個結論
這里主要解釋結論 3,這里的結論不僅適用於 ingress-nginx,也適用於其它使用 nginx 的場景。
結論1:nginx 的端口耗盡時,會返回 502 錯誤(和本文要討論的內容無關)。
結論2:nginx 向已經被服務端主動斷開的連接發送請求,會收到 RST,然后返回 502。
結論3:服務端先於 nginx 斷開連接的情況有兩種,
1)服務端的連接超時時間小於 nginx 中的配置;
2)服務端配置的單個連接的最大請求數小於 nginx 中配置。
2.為什么服務端有超時時間和最大請求數限制?
服務端應用可能是通過本地的 tomcat 或者其它 web 框架對外暴露的,這種情況非常普遍。 這些 Web 服務或者框架通常都有默認的長連接設置。
譬如 tomcat 的相關配置
另外曾經遇到過的 Gunicorn 超時時間只有 2 秒:
3.nginx 的配置與后端服務的配置不一致時
如果做反向代理的 nginx 中配置的連接斷開條件比后端服務設置的條件寬松,那么就容易出現后端服務先斷開連接的情況, 這時候 nginx 轉發請求到 upstream,upstream 會返回 RST,nginx 打印下面的錯誤日志,給客戶端返回 502:
2019/06/13 04:57:54 [error] 3429#3429: *21983075 upstream prematurely closed connection while reading response header from upstream, client: 10.19.167.120, server: XXXX.com, request: "POST XXXX HTTP/1.0", upstream: "http://11.0.29.4:8080/XXXXXX", host: "XXXX.com" 2019/06/13 04:58:34 [error] 3063#3063: *21989359 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 10.19.138.139, server: XXXX.com, request: "POST /api/v1/XXXX HTTP/1.1", upstream: "http://11.0.145.9:8080/api/v1/XXXX", host: "XXXX.com"
4.建議設置
可以調整 nginx 的 upstream 中 keepalive_timeout 和 keepalive_requests,確保 nginx 先於 upstream 斷開連接。只有 nginx 與 upstream 之間使用長連接的時候需要考慮這種情況,並進行類似的設置。
upstream record_upstream { server 127.0.0.1:9091; keepalive 16; keepalive_timeout 58s; # 默認 60 s,根據實際情況調整,建議小於 60s keepalive_requests 98; # 默認 100 個,根據實際情況調整,建議小於 100 } server { ... location /http/ { proxy_pass http://record_upstream; proxy_http_version 1.1; proxy_set_header Connection ""; ... } }
nginx 的 keepalive_timeout 和 keepalive_requests 參數各有兩個:一組屬於 ngx_http_core_module,在 http/server/location 中使用,限制的是 client 與 nginx 之間的連接;另一組是上面使用的,屬於 ngx_http_upstream_module,限制的是 nginx 與 upstream 之間的連接。
5.默認行為
nginx 的 upstream 中沒有明確配置 keepalive,那么無論 client 和 nginx 之間是否長連接,nginx 和 upstream 都是短連接。
用下面的配置觀察:
upstream record_upstream { server 127.0.0.1:9091; #keepalive 3; #keepalive_timeout 58s; #keepalive_requests 98; } server { listen 9000; listen [::]:9000; server_name echo.example; keepalive_requests 2000; keepalive_timeout 60s; location / { proxy_pass http://record_upstream; #proxy_http_version 1.1; #proxy_set_header Connection ""; } }
使用長連接訪問 nginx :
wrk -c 1 -t 1 -d 2s http://127.0.0.1:9000
http-record 收到的請求是 “Connection: close”:
/go/src/Server/echo.go:46: { "RemoteAddr": "172.17.0.1:34522", "Method": "GET", "Host": "record_upstream", "RequestURI": "/", "Header": { "Connection": [ "close" ] }, "Body": "" }
6.參考
https://www.lijiaocn.com/%E9%97%AE%E9%A2%98/2019/12/04/nginx-keep-alive-problem.html