W przypadku zdarzeń wysyłanych przez serwer (SSE), jaka konfiguracja serwera proxy Nginx jest odpowiednia?

21

Przeczytałem kilka różnych pytań na temat tego, która konfiguracja Nginx jest odpowiednia dla SSE i wymyśliłem mylące wyniki dotyczące tego, jakich ustawień użyć:

Jaka jest prawidłowa odpowiedź?

c4urself
źródło

Odpowiedzi:

46

Długotrwałe połączenie

Zdarzenia wysyłane przez serwer (SSE) to długotrwałe połączenie HTTP **, więc na początek potrzebujemy tego:

proxy_http_version 1.1;
proxy_set_header Connection "";

UWAGA: Połączenia TCP w HTTP / 1.1 są domyślnie trwałe, więc ustawienie pustego nagłówka połączenia robi właściwą rzecz i jest sugestią Nginx.

Chunked Transfer-Encoding

Teraz na bok; Odpowiedzi SSE nie ustawiają nagłówka Content-Length, ponieważ nie mogą wiedzieć, ile danych zostanie wysłanych, zamiast tego muszą użyć nagłówka Transfer-Encoding [0] [1], co pozwala na połączenie strumieniowe. Uwaga: jeśli nie dodasz długości treści, większość serwerów HTTP zostanie Transfer-Encoding: chunked;dla Ciebie ustawiona . O dziwo, fragmentowanie HTTP ostrzegało i powoduje zamieszanie.

Zamieszanie wynika z nieco niejasnego ostrzeżenia w sekcji Notatki opisu W3 EventSource:

Ostrzega się także autorów, że dzielenie HTTP może mieć nieoczekiwany negatywny wpływ na niezawodność tego protokołu. Tam, gdzie to możliwe, dzielenie na fragmenty powinno być wyłączone w celu obsługi strumieni zdarzeń, chyba że szybkość wiadomości jest wystarczająco wysoka, aby nie miało to znaczenia.

Co mogłoby prowadzić do przekonania, że Transfer-Encoding: chunked;jest to złe dla SSE. Jednak: niekoniecznie tak jest, jest to tylko problem, gdy twój serwer wykonuje za ciebie części (nie znając informacji o twoich danych). Chociaż większość postów sugeruje, że dodanie chunked_transfer_encoding off;tego nie jest konieczne w typowym przypadku [3].

Buforowanie (prawdziwy problem)

Większość problemów wynika z buforowania między serwerem aplikacji a klientem. Domyślnie [4], wykorzystuje nginx proxy_buffering on(również przyjrzeć uwsgi_bufferingi fastcgi_bufferingw zależności od aplikacji), a może zdecydować się na kawałki buforować że chcesz wyjść do klienta. Jest to zła rzecz, ponieważ psuje się charakter SSE w czasie rzeczywistym.

Jednak zamiast zwracać się proxy_buffering offo wszystko, najlepiej jest (jeśli możesz) dodać X-Accel-Buffering: nonagłówek odpowiedzi w kodzie serwera aplikacji, aby wyłączyć buforowanie tylko dla odpowiedzi opartej na SSE, a nie dla wszystkich odpowiedzi pochodzących z aplikacji serwer. Bonus: będzie to również działać dla uwsgii fastcgi.

Rozwiązanie

Tak więc naprawdę ważnymi ustawieniami są nagłówki odpowiedzi serwera aplikacji:

Content-Type: text/event-stream;
Cache-Control: no-cache;
X-Accel-Buffering: no;

I potencjalnie implementacja jakiegoś mechanizmu pingowania, aby połączenie nie pozostawało zbyt długo bezczynne. Niebezpieczeństwo polega na tym, że Nginx zamyka bezczynne połączenia zgodnie z keepaliveustawieniem.


[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

c4urself
źródło
Czy potrafisz wyjaśnić, na czym polega mechanizm pingowania? Czy to po prostu przekazuje pustą wiadomość do kanału? Skonfigurowałem nagłówki na poziomie nginx i aplikacji, ale wciąż otrzymuję limit czasu 504 od nginx dla dowolnego punktu końcowego źródła zdarzeń.
wgwz
ping to po prostu niektóre (fałszywe) dane wysyłane w odstępach czasu przez połączenie, na kliencie możesz obsłużyć ten ping i zignorować go. UWAGA: jeśli twoje połączenie w ogóle nie działa, ping nie pomoże, coś innego jest nie tak.
c4urself 18.04.17
2
Dodałem nagłówki odpowiedzi zgodnie z sugestią i działa. Nie wprowadziłem żadnych zmian w konfiguracji nginx v1.12 i do tej pory żadnych problemów.
Mikkel
1
Dodanie X-Accel-Buffering: nonagłówka było dla mnie kluczowe, ale co ważne, musiałem zrobić tak, jak napisał @ c4urself: „dodaj buforowanie X-Accel: nie jako nagłówek odpowiedzi w kodzie serwera aplikacji ”. Dodanie tego nagłówka do sekcji lokalizacji w mojej konfiguracji nginx nie zadziałało - cały strumień zdarzeń czekał na wysłanie aż do zakończenia / zakończenia aplikacji.
MDMower
Jest proxy_http_version 1.1; konieczne? Próbuję uruchomić więcej niż 6 strumieni SSE z przeglądarki i dlatego potrzebuję HTTP2.
Bilal Fazlani