Para eventos enviados pelo servidor (SSE), que configuração de proxy do Nginx é apropriada?

20

Eu li várias perguntas diferentes sobre qual configuração do Nginx é apropriada para o SSE e obtive alguns resultados confusos sobre as configurações a serem usadas:

Então, qual é a resposta certa?

c4urself
fonte

Respostas:

44

Conexão de longa duração

Eventos enviados pelo servidor (SSE) são uma conexão HTTP de execução longa **, portanto, para iniciantes, precisamos do seguinte:

proxy_http_version 1.1;
proxy_set_header Connection "";

NOTA: As conexões TCP no HTTP / 1.1 são persistentes por padrão, portanto, definir o cabeçalho de conexão como vazio faz a coisa certa e é a sugestão do Nginx.

Codificação de transferência em pedaços

Agora um aparte; As respostas SSE não definem um cabeçalho de comprimento de conteúdo porque não podem saber quantos dados serão enviados; eles precisam usar o cabeçalho de codificação de transferência [0] [1], o que permite uma conexão de streaming. Observe também: se você não adicionar um Comprimento de conteúdo, a maioria dos servidores HTTP definirá Transfer-Encoding: chunked;para você. Estranhamente, o chunking HTTP alertou e causa confusão.

A confusão decorre de um aviso um tanto vago na seção Notas da descrição W3 EventSource:

Os autores também são alertados que o HTTP chunking pode ter efeitos negativos inesperados na confiabilidade deste protocolo. Sempre que possível, o chunking deve ser desativado para veicular fluxos de eventos, a menos que a taxa de mensagens seja alta o suficiente para que isso não importe.

O que levaria a acreditar que Transfer-Encoding: chunked;é uma coisa ruim para a SSE. No entanto: esse não é necessariamente o caso, é apenas um problema quando seu servidor da web faz o trabalho por você (sem saber informações sobre seus dados). Portanto, embora a maioria das postagens sugira que chunked_transfer_encoding off;isso não seja necessário no caso típico [3].

Buffer (o verdadeiro problema)

A origem da maioria dos problemas é ter qualquer tipo de buffer entre o servidor de aplicativos e o cliente. Por padrão [4], o Nginx usa proxy_buffering on(também dê uma olhada uwsgi_bufferinge fastcgi_bufferingdependendo do seu aplicativo) e pode optar por armazenar em buffer os pedaços que você deseja enviar ao seu cliente. Isso é ruim porque a natureza em tempo real do SSE é interrompida.

No entanto, em vez de procurar proxy_buffering offpor tudo, é realmente melhor (se você puder) adicionar o X-Accel-Buffering: nocabeçalho como resposta no código do servidor de aplicativos para desativar o buffer apenas para a resposta baseada em SSE e não para todas as respostas provenientes do seu aplicativo servidor. Bônus: isso também funcionará para uwsgie fastcgi.

Solução

E, portanto, as configurações realmente importantes são, na verdade, os cabeçalhos de resposta do servidor de aplicativos:

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

E potencialmente a implementação de algum mecanismo de ping para que a conexão não fique ociosa por muito tempo. O perigo disso é que o Nginx fechará as conexões inativas conforme definido usando a keepaliveconfiguração.


[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
fonte
Você pode elaborar qual é o mecanismo de ping? É simplesmente enviar uma mensagem vazia para o canal? Eu configurei os cabeçalhos de nível do nginx e do aplicativo, mas ainda estou recebendo um tempo limite 504 do nginx para qualquer um dos pontos de extremidade da origem de eventos.
wgwz
um ping seria apenas alguns dados (falsos) enviados em um intervalo através da conexão; no cliente, você pode manipular esse ping e ignorá-lo. NOTA: se a sua conexão não estiver funcionando, o ping não ajudará, algo está errado.
C4urself
2
Eu adicionei os cabeçalhos de resposta conforme sugerido e funciona. Não fiz alterações na configuração do nginx v1.12 e até agora não há problemas.
Mikkel
11
Adicionar o X-Accel-Buffering: nocabeçalho foi a chave para mim, mas o mais importante foi que o @ c4urself escreveu: "adicione o X-Accel-Buffering: no como um cabeçalho de resposta no código do servidor de aplicativos ". A adição desse cabeçalho a uma seção de localização em minha configuração do nginx não funcionou - todo o fluxo de eventos aguardou o envio até depois que o aplicativo foi concluído / finalizado.
MDMower 17/01/19
É proxy_http_version 1.1; necessário? Estou tentando executar mais de 6 fluxos SSE de um navegador e, portanto, preciso de HTTP2.
Bilal Fazlani 11/07/19