我有一个“单页”Web 应用程序,它需要客户端和服务器之间的双向通信。
该应用程序最初设计为依赖 WebSockets,但已被修改为使用 SockJS,禁用了 WebSocket 支持。
使用 SockJS 库的客户端倾向于使用“XHR Streaming”作为双向通信的手段。
服务器是一个 Java Web 应用程序,使用 Spring 的 SockJS 服务器实现(来自 Spring 的 WebSocket 模块),托管在 Tomcat 中。
NGINX 用作 Tomcat 的反向代理。
NGINX 的默认配置proxy_buffering
是启用的。
NGINX 的文档没有充分解释缓冲区的行为:如果数据正在(来自 Tomcat)长期流式传输,则在什么情况下会刷新缓冲区(即,当数据实际被推送到客户端时)并不明显HTTP 连接。
我的观察是从服务器推送的数据(对客户端请求的响应)可能位于 NGINX 的缓冲区中,直到该客户端发生下一个服务器生成的 SockJS 心跳。这样做的效果是延迟 25 秒将响应传输到客户端!
我可以通过实验非常可靠地重现此问题 - 行为是确定性的,但我无法解释配置的缓冲区大小、正在传输的数据大小和 NGINX 的行为之间的关系。
我的服务器的职责是生成对客户端命令调用的响应;每个响应的大小会有所不同(从几个字节到几十千字节),但是是独立的。
主要目标是减少对客户端命令的响应延迟。
NGINX 只看到一个长期存在的 HTTP 流;将流的内容划分为单独的命令响应(用于立即发送)需要 NGINX 理解 SockJS 的专有协议,而它不能。
因此,我认为 NGINX 的缓冲策略与我的用例根本不兼容,并计划禁用proxy_buffering
; 这是明智的吗?
NGINX 的文档建议如果proxy_buffering
禁用,上游服务器(Tomcat)将被迫保持 HTTP 响应打开,直到客户端接收到所有数据(这似乎是缓冲的合理定义!)。
因此,NGINX 的文档建议不要禁用proxy_buffering
,因为它可能会浪费上游服务器资源。
但是,因为我的客户端使用 XHR 流,我的服务器已经有义务为每个活动客户端保持打开的 HTTP 连接(对吗?)。因此,禁用proxy_buffering
不应该对我的 Tomcat 服务器产生负面影响;这个对吗?
我认为你在这里的推理是正确的。我有一个具有相同设置的应用程序,sockjs 在 nginx 代理后面。我们看到大量来自具有高延迟的位置的连接断开。在找到这篇文章并关闭
proxy_buffering
我们的断开连接后,问题就解决了。根据我看到的日志,我相信 nginx 缓冲阻止了一些消息在客户端/服务器之间正确发送。