且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Nginx 反向代理 WebSocket 超时

更新时间:2021-11-04 01:54:44

超时可能来自客户端、nginx 或后端.当你说它被服务器端"切断时,我认为这意味着你已经证明它不是客户端.您的 nginx 配置看起来不应该超时 1 天,因此只剩下后端.

The timeout could be coming from the client, nginx, or the back-end. When you say that it is being cut "server side" I take that to mean that you have demonstrated that it is not the client. Your nginx configuration looks like it shouldn't timeout for 1 day, so that leaves only the back-end.

我的第一个建议是,您尝试直接连接到后端并确认问题仍然存在(出于故障排除目的,将 nginx 从图片中移除).请注意,如果使用浏览器不切实际,您可以使用诸如 curl 之类的命令行实用程序来执行此操作.这是一个示例测试命令:

My first suggestion is that you try connecting directly to the back-end and confirm that the problem still occurs (taking nginx out of the picture for troubleshooting purposes). Note that you can do this with command line utilities like curl, if using a browser is not practical. Here is an example test command:

time curl --trace-ascii curl-dump.txt -i -N \
  -H "Host: example.com" \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: BOGUS+KEY+HERE+IS+FINE==" \
  http://127.0.0.1:8080

在我的(工作)案例中,运行上面的示例无限期地保持打开状态(我手动停止了 Ctrl-C),因为 curl 和我的服务器都没有实现超时.但是,当我将其更改为通过 nginx 作为代理(默认超时为 1 分钟)时,如下所示,我在几乎刚好 1 分钟后看到来自 nginx 的 504 响应.

In my (working) case, running the above example stayed open indefinitely (I stopped with Ctrl-C manually) since neither curl nor my server was implementing a timeout. However, when I changed this to go through nginx as a proxy (with default timeout of 1 minute) as shown below I saw a 504 response from nginx after almost exactly 1 minute.

time curl -i -N --insecure \
  -H "Host: example.com" \
  https://127.0.0.1:443/proxied-path

HTTP/1.1 504 Gateway Time-out
Server: nginx/1.14.2
Date: Thu, 19 Sep 2019 21:37:47 GMT
Content-Type: text/html
Content-Length: 183
Connection: keep-alive

<html>
<head><title>504 Gateway Time-out</title></head>
<body bgcolor="white">
<center><h1>504 Gateway Time-out</h1></center>
<hr><center>nginx/1.14.2</center>
</body>
</html>

real    1m0.207s
user    0m0.048s
sys 0m0.042s

其他想法

有人提到尝试 proxy_ignore_client_abort但这应该没有任何区别,除非客户端关闭连接.此外,虽然这可能会使内部连接保持打开状态,但我认为它无法保持端到端流的完整性.

Other ideas

Someone mentioned trying proxy_ignore_client_abort but that shouldn't make any difference unless the client is closing the connection. Besides, although that might keep the inner connection open I don't think it is able to keep the end-to-end stream intact.

您可能想尝试proxy_socket_keepalive,尽管这需要 nginx >= 1.15.6.

You may want to try proxy_socket_keepalive, though that requires nginx >= 1.15.6.

最后,WebSocket 代理文档中有一条提示,提示一个很好的解决方案:

Finally, there's a note in the WebSocket proxying doc that hints at a good solution:

或者,可以将代理服务器配置为定期发送 WebSocket ping 帧以重置超时并检查连接是否仍然有效.

Alternatively, the proxied server can be configured to periodically send WebSocket ping frames to reset the timeout and check if the connection is still alive.

如果您可以控制后端并希望连接无限期地保持打开状态,请定期发送 "ping" 帧到客户端(假设使用 Web 浏览器,则客户端不需要更改,因为它是作为规范的一部分实现的)应该阻止无论连接打开多长时间或涉及多少个中间盒,连接都不会因不活动而关闭(使 proxy_read_timeout 变得不必要).

If you have control over the back-end and want connections to stay open indefinitely, periodically sending "ping" frames to the client (assuming a web browser is used then no change is needed on the client-side as it is implemented as part of the spec) should prevent the connection from being closed due to inactivity (making proxy_read_timeout unnecessary) no matter how long it's open or how many middle-boxes are involved.