Nginx反向代理WebSocket超时问题

4
我正在使用java-websocket来满足我的websocket需求,它在wowza应用程序中运行,并使用nginx进行ssl代理请求到java。问题是,在服务器端恰好1小时后似乎会断开连接。客户端不知道已经断开了相当长的时间。我不想仅仅调整nginx上的超时时间,我想要了解为什么连接被终止,因为直到出现问题之前,套接字一直像往常一样工作。编辑:忘记发布配置了。
location /websocket/ {
    proxy_set_header        X-Real-IP       $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    include conf.d/proxy_websocket;
    proxy_connect_timeout 1d;
    proxy_send_timeout 1d;
    proxy_read_timeout 1d;
}

这包括配置文件:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass                      http://127.0.0.1:1938/;
  • Nginx/1.12.2
  • CentOS Linux 7.5.1804(核心版)
  • Java WebSocket 1.3.8(GitHub
2个回答

7
超时可能来自客户端、nginx 或后端。当您说它被“服务器端”截断时,我理解为您已经证明这不是客户端的问题。您的 nginx 配置看起来不应该超时 1 天,这只留下了后端。

直接测试后端

我的第一个建议是您尝试直接连接到后端并确认问题是否仍会发生(出于故障排除目的将 nginx 排除在外)。请注意,如果使用浏览器不方便,可以使用命令行实用程序(如 curl)进行此操作。以下是示例测试命令:
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响应。

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,但除非客户端关闭连接,否则不应有任何影响。此外,尽管它可能保持内部连接打开,但我认为它无法保持端到端流的完整性。

您可以尝试proxy_socket_keepalive,但这需要nginx >= 1.15.6。

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

或者,可以配置代理服务器定期发送WebSocket ping帧以重置超时并检查连接是否仍然活动。

如果您控制后端并希望连接无限期保持打开状态,则定期向客户端发送“ping”帧(假设使用Web浏览器,则无需在客户端上进行更改,因为它是规范的一部分)应该能够防止由于不活动而关闭连接(使proxy_read_timeout不必要),无论它打开了多长时间或涉及多少中间箱。


这是一个像你说的ping问题。我使用的包有自动发送ping的实现,但在集成时我没有看到它,一直在手动发送ping。一旦我改变了它,就再也没有出现过问题。 - Animaleante

1
很可能是因为您的websocket代理配置需要微调,但既然您问了:
一个反向代理服务器在支持WebSocket时面临一些挑战。其中之一是WebSocket是一种跳跃协议,因此当代理服务器拦截来自客户端的Upgrade请求时,它需要向后端服务器发送自己的Upgrade请求,包括适当的标头。此外,由于WebSocket连接是长期存在的,而不是HTTP使用的典型短期连接,因此反向代理需要允许这些连接保持打开状态,而不是因为空闲而关闭它们。
在处理您的websocket代理的位置指令中,您需要包含标头,以下是Nginx提供的示例:
location /wsapp/ {
    proxy_pass http://wsbackend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
}

这应该会工作了,因为:
NGINX通过允许在客户端和后端服务器之间建立隧道来支持WebSocket。对于NGINX将升级请求从客户端发送到后端服务器,必须显式设置升级和连接标头,就像这个例子一样。
我还建议您查看Nginx Nchan模块,它直接将websocket功能添加到Nginx中。效果很好。

抱歉,我应该在问题中附上我的当前配置。现在已经更新了。 - Animaleante
你尝试过包含 proxy_ignore_client_abort on; 吗? - miknik
我认为这不是你的Nginx设置的问题,或者说至少你在这里提供的信息中没有。你有在任何地方使用SSL吗?我快速查看了一下你链接的websocket服务器的一些代码。注意到了一些类似于keytool -genkey -keyalg RSA -validity 3650的行,它们似乎生成了一个有效期略长于1小时的证书,或者在Nginx中你可能需要查看你的ssl_session_timeout指令。 - miknik
尝试将ssl_session_timeout更改为2小时,但是没有任何变化。接下来要尝试从Java库中切换回提供安全的websocket,以查看问题是与Nginx还是服务器出现了某些奇怪的原因。 - Animaleante
1
@miknik -validity 3650 将证书的过期时间设置为从现在开始的10年(这里的单位是天,而不是秒)。 - jacobq
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接