Я прочитал кучу разных вопросов о том, какая конфигурация Nginx подходит для SSE, и получил некоторые запутанные результаты относительно того, какие настройки использовать:
Так каков правильный ответ?
Серверные события (SSE) - это длительное HTTP-соединение **, поэтому для начала нам понадобится следующее:
proxy_http_version 1.1;
proxy_set_header Connection "";
ПРИМЕЧАНИЕ. TCP-соединения в HTTP / 1.1 по умолчанию являются постоянными, поэтому установка пустого заголовка Connection делает правильные вещи и является предложением Nginx.
Теперь в сторону; Ответы SSE не устанавливают заголовок Content-Length, потому что они не могут знать, сколько данных будет отправлено, вместо этого им нужно использовать заголовок Transfer-Encoding [0] [1], что позволяет использовать потоковое соединение. Также обратите внимание: если вы не добавите Content-Length, большинство HTTP-серверов установят Transfer-Encoding: chunked;
для тебя. Как ни странно, HTTP-фрагменты предостерегают и вызывают путаницу.
Путаница возникла из-за довольно расплывчатого предупреждения в разделе Notes описания 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://tools.ietf.org/html/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://tools.ietf.org/html/rfc7230#section-6.3
[6] https://gist.github.com/CMCDragonkai/6bfade6431e9ffb7fe88