Длительное соединение
Отправленные сервером события (SSE) - это длительное HTTP-соединение **, поэтому для начала нам понадобится следующее:
proxy_http_version 1.1;
proxy_set_header Connection "";
ПРИМЕЧАНИЕ. TCP-соединения в HTTP / 1.1 являются постоянными по умолчанию, поэтому установка пустого заголовка соединения делает правильную вещь и является рекомендацией Nginx.
Chunked Transfer-Encoding
Теперь в стороне; Ответы SSE не устанавливают заголовок Content-Length, потому что они не могут знать, сколько данных будет отправлено, вместо этого им нужно использовать заголовок Transfer-Encoding [0] [1], что позволяет использовать потоковое соединение. Также обратите внимание: если вы не добавите Content-Length, большинство HTTP-серверов будут установлены Transfer-Encoding: chunked;
для вас. Как ни странно, HTTP-чанки предостерегают и вызывают путаницу.
Путаница связана с несколько смутным предупреждением в разделе «Примечания» описания источника событий W3:
Авторы также предупреждают, что разделение на части 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
X-Accel-Buffering: no
заголовка было для меня ключевым, но важно, что я должен был сделать, как писал @ c4urself: «добавить X-Accel-Buffering: no в качестве заголовка ответа в коде вашего сервера приложений ». Добавление этого заголовка в раздел местоположения в моей конфигурации nginx не сработало - весь поток событий ожидал отправки до тех пор, пока приложение не будет завершено / завершено.proxy_http_version 1.1;
необходимым? Я пытаюсь запустить более 6 потоков SSE из браузера и, следовательно, мне нужен HTTP2.