问题背景
自从 Node.js 8.0 开始,http server 增加了一个默认配置 keepAliveTimeout = 5000
,它会自动销毁超过 5 秒的空闲连接。
通过 node-modules/agentkeepalive#58 的示例代码,非常容易重现出服务端 Keep Alive 超时的效果,就是客户端复用「Keep Alive」 的 socket connection 会立刻触发 ECONNRESET
异常,因为此 socket 已经被服务端认为空闲超时强制断开了。
现状
在 Egg 里面为了尽可能避免此问题的发生,综合 Node.js 的 keepAliveTimeout 和 Nginx 的 keepalive_timeout 常规配置,将 httpclient 的 freeSocketKeepAliveTimeout 配置为 4 秒,尽可能避免 ECONNRESET
异常发生。
缺点:无法 100% 解决所有问题。由于这是一个靠约定俗成的配置,和一些不稳定性因素的存在,还是有可能遇到本文所描述的问题。例如服务端 keepalive_timeout 被配置为 4 秒,默认的 Egg 配置也就失效。
更好的解决方案
如果服务端告诉客户端当前 socket connection 会被保留多长时间呢?是否就能解决这个问题了? 如服务端在 Response header 中告诉客户端,当前 socket connection 还可以使用 5 秒,那么客户端通过计算,加上网络延迟 500 毫秒,最终得出当前 socket connection 只能用 4.5 秒。
下一个请求间隔在 4.5 秒内,则客户端继续使用这个 socket connection; 下一个请求间隔在 4.5 秒外,则客户端创建一个新的 socket connection 来请求。
业界事实标准:Keep-Alive Header
Google 一番发现业界有一个事实标准:The Keep-Alive Header。它竟然被所有浏览器都支持!
例如,通过 Response Header 告诉客户端当前 socket connection 还可以使用 5 秒,示例如下:
"Keep-Alive: timeout=5"
nginx 开启
配置 keepalive_timeout 的第二个参数:
keepalive_timeout 5 5;
_特别提示:_nginx 一旦配置了 keepalive_timeout,那么它会覆盖 web server 返回的 Keep-Alive
header。所以你的应用前面有 nginx,那么必须配置第二个参数才会让 Keep-Alive
header 返回给客户端。
Egg 开启
升级到 Egg 的最新版本,默认已经支持。
通过以下 2 个 PR 支持:
- https://github.com/node-modules/agentkeepalive/pull/59
- https://github.com/node-modules/urllib/pull/275
- https://github.com/eggjs/egg/pull/2146
参考
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive
- http://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout
- http://kypros.org/manual/keepalive.html
原文
原始文章来自 egg.js 团队的 Node.js 经验分享:Node.js 专栏
蚂蚁金服实习生招聘
蚂蚁金服2019届实习生校园招聘已经开启,非常欢迎大家加入我们!详情请查看 https://cnodejs.org/topic/5a9627e32580af301494a8f8 招聘帖子。
赞,标题不够吸引人
👍👍 刚好昨天用 egg 调微信支付接口配置错了,导致重复请求,出现 ECONNRESET 错误,还看了半天不明白,这下懂了!感谢!
👍