问题背景

自从 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 的 freeSocketTimeout 配置为 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 秒,示例如下:

  1. "Keep-Alive: timeout=5"

nginx 开启

配置 keepalive_timeout 的第二个参数,设置 Keep-Alive 响应头的值为 5。

  1. # keepalive_timeout timeout [header_timeout];
  2. keepalive_timeout 5 5;

_特别提示:_nginx 一旦配置 keepalive_timeout,那么它会覆盖 web server 返回的 Keep-Alive header。所以你的应用前面有 nginx,那么必须配置第二个参数才会让 Keep-Alive header 返回给客户端。

Egg 开启

升级到 Egg 的最新版本,默认已经支持。

通过以下 2 个 PR 支持:

参考