问题
业务为了负载均衡,前面放了个 Nginx,但最近 502 报警有点频繁,影响了 SLA,因此对这个问题做了较深入的研究。
502 Bad Gateway
简单来说就是 Nginx 找不到一个可用的 upstream,可能的原因有:
- 压根是配置错误
- 连接 upstream server 发生错误/超时
- upstream server 到了处理瓶颈
还有一个重点是轮询了 upstream server 后任然没有一个可用的。但是不管什么原因,都能在 Nginx 的 error log 中找到报错详情。
upstream prematurely closed connection
在 Nginx 中找到错误日志(开了 debug):
1 2 3 4 5 6 7 8 9 10 11 | [debug] 46093#46093: *606728052 http upstream process header [debug] 46093#46093: *606728052 malloc: 00007FB65E3834E0:4096 [debug] 46093#46093: *606728052 posix_memalign: 00007FB65C783D60:4096 @16 [debug] 46093#46093: *606728052 recv: eof:1, avail:1 [debug] 46093#46093: *606728052 recv: fd:65 0 of 4096 [error] 46093#46093: *606728052 upstream prematurely closed connection while reading response header from upstream, client: xxx [debug] 46093#46093: *606728052 http next upstream, 2 [debug] 46093#46093: *606728052 free keepalive peer [debug] 46093#46093: *606728052 free rr peer 4 4 [debug] 46093#46093: *606728052 free rr peer failed: 00007FB65C704F50 1 [debug] 46093#46093: *606728052 finalize http upstream request: 502 |
看了下 Nginx 代码,发现是 c->recv(); 读到的内容为 0,日志中也有显示 recv: fd:65 0 of 4096
,说明没有获取到 response
同时也查了 Tomcat access 日志,请求还没到 Tomcat。看情况就像日志里面说的,连接被断掉了。
一开始怀疑是网络原因导致连接断掉了,但出现较频繁,期间内网也无网络故障,应该和网络无关。
再看日志发现报错前后有很多 keepalive 信息,尝试关掉 keepalive,502 就没了,但这会对性能有一定影响,感觉有点因噎废食了,还得继续研究。
keepalive
HTTP 持久连接(HTTP persistent connection,也称作 HTTP keep-alive 或 HTTP connection reuse)是使用同一个 TCP 连接来发送和接收多个 HTTP 请求/应答,而不是为每一个新的请求/应答打开新的连接的方法。
一般 Nginx 会配置 keepalive 以提高性能:
1 2 3 4 5 6 | upstream servers_test { server 127.0.0.1:5001 max_fails=1 fail_timeout=10s weight=1; server 127.0.0.1:5002 max_fails=1 fail_timeout=10s weight=1; keepalive 10; } |
Syntax: keepalive connections;
Activates the cache for connections to upstream servers.
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.
可以配置每个 worker 对 upstream servers 最大长连接数量。同时这个长连接受 keepalive_requests(默认100) 和 keepalive_timeout(默认60s)配置的影响。
但 keepalive 也有缺陷,会加重 webserver 的负担,因为需要绑定一定数量的线程或者进程来维持长链接。注意 keepalive 并没有连接复用(即同一时间窗口不能处理多个请求,这个在 HTTP/2 中才实现),仅节省了新建/关闭连接的开销,类似连接池了。所以 webserver 一般都有类似 nginx 的 keepalive_requests、keepalive_timeout 配置,让空闲的连接断掉。
查看了 upstream(tomcat) 配置的 timeout 是20s,lighttpd 的 requests 是16,timeout 是 5s,都远小于 nginx 的配置。
原因就清楚了,upstream servers 先断了 keepalive 的长连接,但 nginx 仍使用了这个已经断掉的连接。
至于 nginx 为什么不主动检测一下连接是否可用呢?猜测应该是性能原因,一直检查连接池中的连接是否可用没必要,keepalive 协议本身也没业务心跳啥的。PS:商业版的 ngx_http_upstream_hc_module 可以主动监测(世界加钱可及)。
proxy_next_upstream
那么 Nginx 如何保证高可用呢?答案是重试。这就涉及另一个重要配置 proxy_next_upstream,在什么情况下进行重试,默认为 error timeout。按理说我们这个场景应该会重试,但没有重试。这是因为 Nginx 较新版本(1.9.13)多了个 non_idempotent 配置,默认 POST
, LOCK
, PATCH
等请求不重试,因为这些操作是非幂等操作,会对服务端数据造成影响(比如发消息接口,重试可能会重复发消息)。日志中也发现 502 的全都是 POST 请求。
要完全解决这个问题就需要对 proxy_next_upstream 配置簇深入看看,我们先做个测试。 Continue Reading...