对于服务器发送事件(SSE),适合的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分块传输,因为它可能对协议的可靠性产生意外的负面影响。
这可能会让人认为,对于SSE来说,Transfer-Encoding: chunked; 是件坏事。然而事实并非如此,只有当您的Web服务器为您进行分块(不了解您的数据信息)时才会出现问题。因此,尽管大多数文章建议添加chunked_transfer_encoding off;,但在典型情况下这是不必要的[3]。
缓冲(真正的问题)
大多数问题出现在应用服务器和客户端之间存在任何形式的缓冲时。默认情况下[4],Nginx使用proxy_buffering on(根据您的应用程序还可以查看uwsgi_buffering和fastcgi_buffering),并且可能选择缓冲您要发送给客户端的数据块。这是件糟糕的事情,因为SSE的实时性将被破坏。
然而,与其将proxy_buffering off应用于所有内容,实际上最好(如果您能够)在应用服务器代码中添加X-Accel-Buffering: no作为响应头,仅将缓冲关闭用于基于SSE的响应,而不是所有来自应用服务器的响应。额外加分:这也适用于uwsgifastcgi

解决方案

因此,真正重要的设置实际上是应用服务器的响应头。

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


1你能详细解释一下ping机制是什么吗?它只是向通道发送一个空消息吗?我已经设置了nginx和应用程序级别的头部,但是对于任何事件源端点,我仍然从nginx那里得到504超时错误。 - wgwz
1一个“ping”只是在连接上以一定的时间间隔发送一些(虚假的)数据,在客户端你可以处理这个“ping”并忽略它。注意:如果你的连接根本不工作,那么“ping”是无济于事的,其他地方出了问题。 - c4urself
4我按照建议添加了响应头,现在它可以正常工作了。我没有对nginx v1.12的配置进行任何更改,目前还没有出现任何问题。 - Mikkel
6在我的情况下,添加X-Accel-Buffering: no头信息是关键,但更重要的是,我必须像 @c4urself 写的那样去做:"在你的应用服务器代码中将 X-Accel-Buffering: no 添加为一个响应头"。将这个头信息添加到我 nginx 配置的位置部分并不能生效 -- 整个事件流会等待应用完成/终止后才被发送出去。 - MDMower
proxy_http_version 1.1; 是必需的吗?我正在尝试从浏览器运行超过6个SSE流,因此我需要HTTP2。 - Bilal Fazlani
嗨@c4urself。这是一个很好的解释。考虑以下使用案例:所有的头部信息,无论是在nginx还是HTTP Go服务器上,都按照推荐设置。当SLB配置为HTTP2.0时,连接不是持久的,并且会间断性地中断。然而,当SLB切换回HTTP1.1时,一切都正常工作。这里有什么问题? - Somesh

  • 相关问题