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