Welche Nginx-Proxy-Konfiguration ist für Server-Sent Events (SSE) geeignet?

19

Ich habe eine Reihe verschiedener Fragen zu der für SSE geeigneten Nginx-Konfiguration gelesen und einige verwirrende Ergebnisse hinsichtlich der zu verwendenden Einstellungen erzielt:

Also, was ist die richtige Antwort?

c4urself
quelle

Antworten:

43

Langfristige Verbindung

Server-Sent Events (SSE) sind eine lang laufende HTTP-Verbindung **. Für den Anfang benötigen wir also Folgendes:

proxy_http_version 1.1;
proxy_set_header Connection "";

ANMERKUNG: TCP-Verbindungen in HTTP / 1.1 sind standardmäßig persistent. Wenn Sie also den Verbindungsheader auf leer setzen, ist dies das Richtige und der Nginx-Vorschlag.

Chunked Transfer-Encoding

Jetzt beiseite; SSE-Antworten legen keinen Content-Length-Header fest, da sie nicht wissen können, wie viele Daten gesendet werden, sondern stattdessen den Transfer-Encoding-Header [0] [1] verwenden müssen, der eine Streaming-Verbindung ermöglicht. Beachten Sie auch: Wenn Sie keine Inhaltslänge hinzufügen, werden die meisten HTTP-Server Transfer-Encoding: chunked;für Sie eingerichtet. Seltsamerweise warnt HTTP-Chunking vor und sorgt für Verwirrung.

Die Verwirrung rührt von einer etwas vagen Warnung im Abschnitt "Notizen" der W3 EventSource-Beschreibung her:

Autoren werden auch darauf hingewiesen, dass HTTP-Chunking unerwartete negative Auswirkungen auf die Zuverlässigkeit dieses Protokolls haben kann. Wenn möglich, sollte das Chunking für die Bereitstellung von Ereignisströmen deaktiviert werden, es sei denn, die Nachrichtenrate ist hoch genug, damit dies keine Rolle spielt.

Was zu der Annahme führen würde, dass dies Transfer-Encoding: chunked;eine schlechte Sache für SSE ist. Dies ist jedoch nicht unbedingt der Fall, es ist nur ein Problem, wenn Ihr Webserver das Chunking für Sie durchführt (ohne Informationen über Ihre Daten zu kennen). Während die meisten Posts vorschlagen, dass chunked_transfer_encoding off;dies im typischen Fall nicht erforderlich ist [3].

Pufferung (das eigentliche Problem)

Wo die meisten Probleme herkommen, ist eine Art Pufferung zwischen dem App-Server und dem Client. Standardmäßig [4] verwendet Nginx proxy_buffering on(siehe auch uwsgi_bufferingund fastcgi_bufferingabhängig von Ihrer Anwendung) und puffert möglicherweise die Chunks, die Sie an Ihren Client senden möchten. Dies ist eine schlechte Sache, da der Echtzeitcharakter von SSE bricht.

Anstatt proxy_buffering offfür alles zu drehen , ist es jedoch am besten (wenn Sie dazu in der Lage sind), den X-Accel-Buffering: noHeader als Antwort in Ihren Anwendungsserver-Code einzufügen, um die Pufferung nur für die SSE-basierte Antwort und nicht für alle Antworten aus Ihrer App zu deaktivieren Server. Bonus: Dies funktioniert auch für uwsgiund fastcgi.

Lösung

Die wirklich wichtigen Einstellungen sind also die Antwort-Header des App-Servers:

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

Und möglicherweise die Implementierung eines Ping-Mechanismus, damit die Verbindung nicht zu lange inaktiv bleibt. Die Gefahr besteht darin, dass Nginx Leerlaufverbindungen schließt, wie mit der keepaliveEinstellung festgelegt.


[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
quelle
Können Sie den Ping-Mechanismus näher erläutern? Schiebt es einfach eine leere Nachricht an den Kanal? Ich habe die Header auf Nginx- und App-Ebene eingerichtet, erhalte jedoch immer noch eine Zeitüberschreitung von 504 von Nginx für einen der Ereignisquellen-Endpunkte.
wgwz
Ein Ping besteht nur aus falschen Daten, die in bestimmten Abständen über die Verbindung gesendet werden. Auf dem Client können Sie diesen Ping verarbeiten und ignorieren. HINWEIS: Wenn Ihre Verbindung überhaupt nicht funktioniert, hilft Ping nicht, etwas anderes stimmt nicht.
c4urself
2
Ich habe die Antwort-Header wie vorgeschlagen hinzugefügt und es funktioniert. Ich habe keine Änderungen an der Konfiguration von Nginx v1.12 vorgenommen und bisher keine Probleme.
Mikkel
1
Das Hinzufügen des X-Accel-Buffering: noKopfes war der Schlüssel für mich, aber wichtiger ist , was ich tun musste , wie @ c4urself schrieb: „fügen Sie das X-Accel-Buffering: Nein als Antwort - Header in Ihrem Anwendungsserver - Code “. Das Hinzufügen dieses Headers zu einem Speicherortabschnitt in meiner Nginx-Konfiguration funktionierte nicht - der gesamte Ereignisstrom wartete darauf, gesendet zu werden, bis die Anwendung beendet / beendet wurde.
MDMower
Ist proxy_http_version 1.1; notwendig Ich versuche, mehr als 6 SSE-Streams über einen Browser auszuführen, und benötige daher HTTP2.
Bilal Fazlani