Pour les événements envoyés par le serveur (SSE), quelle configuration de proxy Nginx est appropriée?

20

J'ai lu un tas de questions différentes sur la configuration Nginx appropriée pour SSE et j'ai trouvé des résultats déroutants concernant les paramètres à utiliser:

Alors, quelle est la bonne réponse?

c4ourself
la source

Réponses:

45

Connexion longue durée

Les événements envoyés par le serveur (SSE) sont une connexion HTTP de longue durée **, donc pour les débutants, nous avons besoin de ceci:

proxy_http_version 1.1;
proxy_set_header Connection "";

REMARQUE: les connexions TCP dans HTTP / 1.1 sont persistantes par défaut, donc définir l'en-tête Connection sur vide fait la bonne chose et est la suggestion de Nginx.

Codage de transfert en morceaux

Maintenant un aparté; Les réponses SSE ne définissent pas d'en-tête Content-Length car elles ne peuvent pas savoir combien de données seront envoyées, mais doivent utiliser l'en-tête Transfer-Encoding [0] [1], ce qui permet une connexion en streaming. Notez également: si vous n'ajoutez pas de longueur de contenu, la plupart des serveurs HTTP seront définis Transfer-Encoding: chunked;pour vous. Étrangement, HTTP chunking a mis en garde et cause de la confusion.

La confusion provient d'un avertissement quelque peu vague dans la section Notes de la description de W3 EventSource:

Les auteurs sont également avertis que le découpage HTTP peut avoir des effets négatifs inattendus sur la fiabilité de ce protocole. Dans la mesure du possible, la segmentation doit être désactivée pour servir les flux d'événements, sauf si le taux de messages est suffisamment élevé pour que cela n'ait pas d'importance.

Ce qui ferait croire que Transfer-Encoding: chunked;c'est une mauvaise chose pour l'ESS. Cependant: ce n'est pas nécessairement le cas, c'est seulement un problème lorsque votre serveur Web effectue le segmentage pour vous (sans connaître les informations sur vos données). Ainsi, alors que la plupart des articles suggèrent d'ajouter chunked_transfer_encoding off;ce n'est pas nécessaire dans le cas typique [3].

Mise en mémoire tampon (le vrai problème)

La plupart des problèmes proviennent de la mise en mémoire tampon de tout type entre le serveur d'applications et le client. Par défaut [4], Nginx utilise proxy_buffering on(regardez également uwsgi_bufferinget en fastcgi_bufferingfonction de votre application) et peut choisir de mettre en mémoire tampon les morceaux que vous souhaitez envoyer à votre client. C'est une mauvaise chose car la nature en temps réel de SSE est interrompue.

Cependant, au lieu de tourner proxy_buffering offpour tout, il est préférable (si vous le pouvez) d'ajouter l' X-Accel-Buffering: noen-tête de réponse dans votre code de serveur d'applications pour désactiver uniquement la mise en mémoire tampon pour la réponse basée sur SSE et non pour toutes les réponses provenant de votre application. serveur. Bonus: cela fonctionnera également pour uwsgiet fastcgi.

Solution

Et donc les paramètres vraiment importants sont en fait les en-têtes de réponse du serveur d'applications:

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

Et potentiellement l'implémentation d'un mécanisme de ping afin que la connexion ne reste pas inactive trop longtemps. Le danger est que Nginx ferme les connexions inactives comme défini en utilisant le keepaliveparamètre.


[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

c4ourself
la source
Pouvez-vous expliquer en quoi consiste le mécanisme de ping? S'agit-il simplement de pousser un message vide vers la chaîne? J'ai configuré les en-têtes de niveau nginx et d'application, mais j'obtiens toujours un délai d'expiration 504 de nginx pour l'un des points de terminaison de la source d'événements.
wgwz
un ping ne serait que des données (fausses) envoyées à un intervalle sur la connexion, sur le client, vous pouvez gérer ce ping et l'ignorer. REMARQUE: si votre connexion ne fonctionne pas du tout, le ping ne vous aidera pas, quelque chose d'autre ne va pas.
c4urself
2
J'ai ajouté les en-têtes de réponse comme suggéré et cela fonctionne. Je n'ai apporté aucune modification à la configuration de nginx v1.12 et jusqu'à présent, aucun problème.
Mikkel le
1
L'ajout de l'en- X-Accel-Buffering: notête était essentiel pour moi, mais surtout, je devais faire comme @ c4urself a écrit: "ajoutez le X-Accel-Buffering: no comme en-tête de réponse dans votre code de serveur d'application ". L'ajout de cet en-tête à une section d'emplacement dans ma configuration nginx n'a pas fonctionné - l'ensemble du flux d'événements a attendu d'être envoyé jusqu'à la fin / la fin de l'application.
MDMower
Est proxy_http_version 1.1; nécessaire? J'essaie d'exécuter plus de 6 flux SSE à partir d'un navigateur et j'ai donc besoin de HTTP2.
Bilal Fazlani