我读过很多关于哪种 Nginx 配置适合 SSE 的不同问题,并且对应该使用什么设置得出了一些令人困惑的结果:
- https://stackoverflow.com/questions/17529421/sending-server-sent-events-through-a-socket-in-c
- https://stackoverflow.com/questions/13672743/eventsource-server-sent-events-through-nginx
- https://stackoverflow.com/questions/21630509/server-sent-events-connection-timeout-on-node-js-via-nginx
那么正确答案是什么?
答案1
长时间运行的连接
服务器发送事件 (SSE) 是一种长期运行的 HTTP 连接**,因此首先我们需要以下内容:
proxy_http_version 1.1;
proxy_set_header Connection "";
注意:HTTP/1.1 中的 TCP 连接默认是持久的,因此将 Connection 标头设置为空是正确的,也是 Nginx 的建议。
分块传输编码
现在顺便说一句;SSE 响应不会设置 Content-Length 标头,因为它们不知道将发送多少数据,而是需要使用 Transfer-Encoding 标头[0][1],这允许流式连接。还请注意:如果您不添加 Content-Length,大多数 HTTP 服务器都会Transfer-Encoding: chunked;
为您设置。奇怪的是,HTTP 分块会发出警告并引起混乱。
这种混淆源于 W3 EventSource 描述的注释部分中的一个模糊的警告:
作者还需注意,HTTP 分块可能会对该协议的可靠性产生意想不到的负面影响。在可能的情况下,应禁用分块来处理事件流,除非消息速率足够高,因此不会产生影响。
这会让人觉得Transfer-Encoding: chunked;
这对 SSE 来说是一件坏事。然而:情况不一定如此,只有当您的网络服务器为您进行分块(不知道您的数据信息)时,这才会成为问题。因此,虽然大多数帖子会建议添加chunked_transfer_encoding off;
此功能,但在典型情况下没有必要[3]。
缓冲(真正的问题)
大多数问题都源于应用服务器和客户端之间的缓冲类型。默认情况下[4],Nginx 使用
proxy_buffering on
(也请查看uwsgi_buffering
并fastcgi_buffering
取决于您的应用程序)并可能选择缓冲您想要发送到客户端的块。这是一件坏事,因为 SSE 的实时性被破坏了。
但是,proxy_buffering off
实际上,最好(如果可以的话)X-Accel-Buffering: no
在应用服务器代码中添加作为响应标头,以便只关闭基于 SSE 的响应的缓冲,而不是关闭来自应用服务器的所有响应的缓冲,而不是关闭所有响应的缓冲。额外好处:这也适用于uwsgi
和fastcgi
。
解决方案
因此真正重要的设置实际上是应用服务器响应标头:
Content-Type: text/event-stream;
Cache-Control: no-cache;
X-Accel-Buffering: no;
并且可能实现某种 ping 机制,以便连接不会闲置太久。这样做的危险在于 Nginx 将按照设置关闭空闲连接keepalive
。
[0]https://www.rfc-editor.org/rfc/rfc2616#section-3.6
[1]https://en.wikipedia.org/wiki/Chunked_transfer_encoding
[2]https://www.w3.org/TR/2009/WD-eventsource-20091029/#text-event-stream
[3]https://github.com/whatwg/html/issues/515
[4]http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering
[5]https://www.rfc-editor.org/rfc/rfc7230#section-6.3
[6]https://gist.github.com/CMCDragonkai/6bfade6431e9ffb7fe88