背景
近期做了 nginx 的监控,发现有些 5xx 的错误,主要是502和503,出现 503 是因为请求并发超出限制,这个需要提醒客户自行降低并发,之后也有客户反馈偶尔会有 502 情况,虽然很少,他的接口每天 10 来个,整个系统每天 100 来个,虽然中途加了一些实例看起来缓解了问题,但这个客户时不时问一下,还是挺让人纠结的,所以利用节前的空余时间找资料研究一下。
详细情况
nginx 在报 502 时,error日志是下面这两个情况:
1、Connection reset by peer
2019/01/31 11:50:40 [error] 19324#0: *65019204 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 1.119.136.18, server: xxx.com, request: "POST /path/abc HTTP/1.0", upstream: "htt
p://xxx:12345/path/abc", host: "xxx.com"
2、upstream prematurely closed connection
2019/01/31 13:39:04 [error] 19323#0: *65264092 upstream prematurely closed connection while reading response header from upstream, client: 42.62.19.174, server: xxx.com, request: "POST /path/abc HTTP/1.1", upstream: "http://xxx:12345/path/abc", host: "xxx.com"
然后 node 服务未见异常,这个也可能是出错后没打印出来。。。
问题分析
1、从问题的描述上看,都是因为上游服务(node服务)断开了连接,网上虽然说第一种情况可能是客户端主动关闭了连接,但从nginx请求日志上看,各个客户接口都有,而且在服务重启时,就会触发,所以可以确定是我们本身node服务的问题;
2、网上的资料显示,upstream keepAlive的配置需要小一些,原因是过大的值会导致过多空闲的长连接,如果上游长连接断开并且处理半关闭状态,即nginx还未感知到那个长连接已经不可用了,如果这时再往这个长连接发数据,就会触发到上述问题2;
3、至于为什么node服务会主动断开长连接,原因是node 8.0.0开始,新增了keepAliveTimeout参数,并且默认值为5000ms,这个参数的作用是设置长连接允许空闲的时间,如果空闲时间超过配置,就主动断开长连接,然后连接就会等待发起端确认并关闭;
4、长连接确实需要空闲时回收,防止占用太多资源,但最好是发送端主动发起关闭,这样不会导致连接被过早关闭的问题,这里的场景下,nginx作为长连接的发起者,可以选择主动断开,但目前服务使用的nginx版本,官方不提供upstream keepAliveTimeout,得引入一些第三方模块,除非升级到1.15.3,两者都需要重启nginx,而且出问题对稳定性影响比较大,所以还是选择先调整node服务;
问题修复
先说下折腾的过程,防止之后还踩坑。先是怀疑 upstream keep_alive 数值配置过大,当时是 64,改为 5 还是继续不定时出现 502;
另外还调整了 http, server, location 三者中的 keep_alive_timeout,nginx 文档上说该项配置的默认值是75s,我想既然 node 8.0 之后默认是 5s,那 nginx 调整为4s就可以避免过早断开的问题了,然而配置 4s 后还是没能解决问题,最后才发现这里的 keep_alive_timeout 是指 nginx 跟客户端的,不是跟 upstream 的,upstream 的配置需要在 1.15.3 之后才有官方支持,旧版本需要用第三方模块。文档如下:
经过一番搜索和查询,问题终于浮出水面,即 node keepAliveTimeout 配置不当,根据 node.js 官方文档,可能通过指定 keepAliveTimout 为 0 来关闭这个功能。
node 服务的调整如下:
const server = app.listen(options, function () {
// A value of 0 will disable the keep-alive timeout behavior on incoming connections
// default 5000 (5 seconds) 从node v8.0.0开始默认是5秒,开启时如果数值小于nginx的keepAliveTimeout的话,在低并发时有一定概率会使主动断开与nginx代理的长连接,导致请求502
// 而且目前nginx所用的1.12.1版本不支持upstream的keepAliveTimeout配置,所以暂时关闭node服务的keepAliveTimeout
server.keepAliveTimeout = 0
console.info('listen', options.port, 'process.version', process.version)
})
更新上线后,nginx 不再有 502 的情况了
参考
有关服务端主动关闭socket带来的几个问题分析--tcp四次握手半关闭问题导致
node http 文档
nginx长连接文档中文解析
ngx_http_upstream_module
ngx_http_core_module
本文由 Chakhsu Lau 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。