Para los eventos enviados por el servidor (SSE), ¿qué configuración de proxy Nginx es adecuada?

Respuestas:

45

Conexión de larga duración

Los eventos enviados por el servidor (SSE) son una conexión HTTP de larga duración **, por lo que para empezar necesitamos esto:

proxy_http_version 1.1;
proxy_set_header Connection "";

NOTA: Las conexiones TCP en HTTP / 1.1 son persistentes de manera predeterminada, por lo que configurar el encabezado de la conexión en vacío hace lo correcto y es la sugerencia de Nginx.

Transferencia-codificación fragmentada

Ahora un aparte; Las respuestas SSE no establecen un encabezado Content-Length porque no pueden saber cuántos datos se enviarán, sino que necesitan usar el encabezado Transfer-Encoding [0] [1], lo que permite una conexión de transmisión. También tenga en cuenta: si no agrega una longitud de contenido, la mayoría de los servidores HTTP lo configurarán Transfer-Encoding: chunked;. Curiosamente, la fragmentación de HTTP advierte y causa confusión.

La confusión surge de una advertencia algo vaga en la sección de Notas de la descripción de W3 EventSource:

También se advierte a los autores que la fragmentación de HTTP puede tener efectos negativos inesperados sobre la confiabilidad de este protocolo. Siempre que sea posible, se debe deshabilitar la fragmentación para servir secuencias de eventos a menos que la velocidad de los mensajes sea lo suficientemente alta como para que esto no importe.

Lo que llevaría a creer que Transfer-Encoding: chunked;es malo para SSE. Sin embargo: este no es necesariamente el caso, solo es un problema cuando su servidor web está haciendo el fragmento por usted (sin conocer la información sobre sus datos). Entonces, aunque la mayoría de las publicaciones sugerirán que agregar chunked_transfer_encoding off;esto no es necesario en el caso típico [3].

Buffering (el verdadero problema)

De donde provienen la mayoría de los problemas es tener algún tipo de almacenamiento en búfer entre el servidor de aplicaciones y el cliente. Por defecto [4], Nginx usa proxy_buffering on(también eche un vistazo uwsgi_bufferingy fastcgi_bufferingdependiendo de su aplicación) y puede optar por almacenar los fragmentos que desea enviar a su cliente. Esto es algo malo porque la naturaleza en tiempo real de SSE se rompe.

Sin embargo, en lugar de recurrir proxy_buffering offa todo, en realidad es mejor (si puede) agregar el X-Accel-Buffering: noencabezado de respuesta en el código del servidor de su aplicación para desactivar solo el almacenamiento en búfer para la respuesta basada en SSE y no para todas las respuestas que provienen de su aplicación servidor. Bonificación: esto también funcionará para uwsgiy fastcgi.

Solución

Entonces, la configuración realmente importante son los encabezados de respuesta del servidor de aplicaciones:

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

Y, potencialmente, la implementación de algún mecanismo de ping para que la conexión no permanezca inactiva durante demasiado tiempo. El peligro de esto es que Nginx cerrará las conexiones inactivas según lo establecido mediante la keepaliveconfiguración.


[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
fuente
¿Puedes explicar en qué consiste el mecanismo de ping? ¿Simplemente está enviando un mensaje vacío al canal? He configurado los encabezados de nivel de aplicación y nginx, pero todavía obtengo un tiempo de espera 504 de nginx para cualquiera de los puntos finales de origen de eventos.
wgwz
un ping solo sería un dato (falso) enviado a intervalos a través de la conexión, en el cliente puede manejar este ping e ignorarlo. NOTA: si su conexión no funciona, hacer ping no ayudará, algo más está mal.
c4urself
2
Agregué los encabezados de respuesta como se sugiere y funciona. No hice cambios en la configuración de nginx v1.12 y hasta ahora no tuve problemas.
Mikkel
1
Agregar el X-Accel-Buffering: noencabezado fue clave para mí, pero lo más importante es que tuve que hacer lo que @ c4urself escribió: "agregue X-Accel-Buffering: no como encabezado de respuesta en el código del servidor de su aplicación ". Agregar este encabezado a una sección de ubicación en mi configuración de nginx no funcionó: toda la secuencia del evento esperó ser enviada hasta después de que la aplicación finalizó / finalizó.
MDMower
Es proxy_http_version 1.1; necesario? Estoy tratando de ejecutar más de 6 secuencias SSE desde un navegador y, por lo tanto, necesito HTTP2.
Bilal Fazlani