DEV Community

Uriel dos Santos Souza
Uriel dos Santos Souza

Posted on • Updated on

SSE Server Sent Events Eventos enviados pelo servidor

server sent events image

Eventos enviados pelo servidor?
Hoje basicamente tudo é envio de dados!
Mas envio de dados tem custos!

Seja por:

Polling(e tempos em tempos o navegador pergunta ao servidor se tem algo novo)

WebSocket o queridinho(é um long polling, o cliente se conecta ao servidor e uma conexão fica aberta entre ambos, as vezes o cliente pede alguma info ao servidor)

Tanto polling como websocket são bons e servem bem ao seus propósitos.

Polling, pedir informações de tempos em tempos custa banda e conexões do servidor. Se o número de clientes for pequeno, tudo bem!? E se o número for bizarramente grande? Se seu servidor não aguentar o número de conexões vai cair(senhor obvio).

Websocket é ótimo, para um chat, onde cliente sempre ou quase sempre fala com o servidor. Eu creio que seja perfeito para chats e aplicações onde a comunicação constante entre clientes e servidores seja constante. Ele economiza um pouco de banda, mas não conexões, embora seja uma conexão diferente da http(websocket é um padrão próprio) ele é full-duplex(parece algo tão legal, uma geladeira gigante rsrs)
https://pt.wikipedia.org/wiki/WebSocket

Mas se você não precisa de um chat! - Eu só quero atualizar um gráfico! Não quero ter que implementar um protocolo(websocket) só pra isso!

Vamos a uma explicação mais séria:

Ajax Polling:

Um cliente solicita uma página da Web a partir de um servidor usando HTTP regular.
O cliente recebe a página web solicitada e executa o JavaScript na página que solicita um arquivo do servidor em intervalos regulares (por exemplo, 0,5 segundos).
O servidor calcula cada resposta e o envia de volta, assim como o tráfego HTTP normal.

Ajax Long-Polling:

Um cliente solicita uma página da Web a partir de um servidor usando HTTP regular.
O cliente recebe a página web solicitada e executa o JavaScript na página que solicita um arquivo do servidor.
O servidor não responde imediatamente com as informações solicitadas, mas aguarda até que haja novas informações disponíveis.
Quando há novas informações disponíveis, o servidor responde com as novas informações.
O cliente recebe as novas informações e imediatamente envia outra solicitação para o servidor, reiniciando o processo.

Eventos enviados pelo servidor HTML5 (SSE) / EventSource:

Um cliente solicita uma página da Web a partir de um servidor usando HTTP regular.
O cliente recebe a página web solicitada e executa o JavaScript na página que abre uma conexão com o servidor.
O servidor envia um evento ao cliente quando há novas informações disponíveis.

Você quer usar um servidor que tenha um loop de events
Não é possível conectar-se a um servidor de outro domínio

Websockets HTML5:

Um cliente solicita uma página da Web de um servidor usando http regular (veja HTTP acima).
O cliente recebe a página web solicitada e executa o JavaScript na página que abre uma conexão com o servidor.
O servidor e o cliente agora podem enviar-se mutuamente mensagens quando novos dados (de cada lado) estão disponíveis.

Tráfego em tempo real do servidor para o cliente e do cliente para o servidor
Você quer usar um servidor que tenha um loop de eventos

Com o WebSockets é possível conectar-se a um servidor de outro domínio.
Também é possível usar um servidor de websocket hospedado de terceiros, por exemplo, Pusher ou outros. Desta forma, você só terá que implementar o lado do cliente, o que é muito fácil!
OBS: WebSocket muda o protocolo de HTTP para websocket!

Vamos usar o SSE!

SSE eventos enviados pelo servidor!
Ele é unidirecional e HTTP puro

O LinkedIn usa SSE para seu serviço de mensagens, o Mapbox usa SSE para exibir dados de mapa ao vivo.
Da pra fazer CHAT em tempo real com SSE.

  • Mas parece bom demais para ser verdade, como implementar isso?

Irei utilizar um exemplo do meu tutorial que esta no youtube.
Nele, que fiz antes de escrever(e sem pensar em escrever sobre isso) eu falo um pouco mais sobre, mas vou deixar uns trechos de código aqui:

OBS: isso é um tutorial simples. SSE é mais que isso, este tutorial serve para te iniciar sobre o assunto SSE. Mas para começar este funciona!

Utilizando express e nodejs é bastante simples!
Mas primeiro temos que saber, todos os navegadores aceitam!
Você vai precisar da API eventSource fornecida pelos navegadores, implementar ela é complicadíssimo, veja o código abaixo:

//https://developer.mozilla.org/pt-BR/docs/Web/API/EventSource

const source = new EventSource('/eventos');
//colocamos em source a instancia eventSouce
//('/eventos') é a URL do meu servidor express que aceita as conexões 

source.addEventListener('message', message => {
//neste caso estou adicionando um evento, assim que
// chegar uma mensagem pela API eventSource, print no console //tudo
console.log('Serve sent', message);


document.querySelector(".content").innerHTML = message.data;
//aqui é simples, só estou mostrando na tela uma parte do que veio na mensagem do servidor! 

})
Enter fullscreen mode Exit fullscreen mode

Complicadíssimo como eu disse!

Agora no servidor express:

app.get('/eventos', (req, res) => {

  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Access-Control-Allow-Origin': '*',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',

  });



res.write('retry: 10000\n\n');

  //https://docs.mongodb.com/manual/changeStreams
  //usando o mongo que emite uma mudança na coleção

user.SenhaUSer.watch().on('change', (mudanca)=>{


  const {operationType} = mudanca; 


 res.write(`data:${operationType}\n\n`)
//server events só funciona com texto

 res.flushHeaders()

});
})
Enter fullscreen mode Exit fullscreen mode

'Access-Control-Allow-Origin': '*'
Permissão para que qualquer navegador/api acesse. OPCIONAL.

'Cache-Control': 'no-cache'
Usado como medida de segurança para evitar o armazenamento em cache dos dados que enviamos

'Connection': 'keep-alive'
Usado para evitar que o navegador feche a conexão após receber todos os dados!

'Content-Type': 'text/event-stream'

Usado para informar ao navegador que estamos enviando eventos enviados pelo servidor

Cada evento é separado por duas linhas vazias (\n) e consiste em vários campos opcionais.

O campo data, que pode ser repetido para denotar várias linhas na mensagem é usado para o conteúdo do evento.

O campo event permite especificar tipos de eventos personalizados, podem ser usados ​​para disparar diferentes manipuladores de eventos no cliente.

Os outros dois campos, id e retry, são usados ​​para configurar o comportamento do mecanismo de reconexão automática. Este é um dos recursos mais interessantes dos eventos enviados pelo servidor. Ele garante que quando a conexão for interrompida ou fechada pelo servidor, o cliente tentará se reconectar automaticamente, sem qualquer intervenção do usuário.

write é usado para evitar o fechamento da conexão do servidor com um send, json, end.

O campo retry é usado para especificar o tempo mínimo, em segundos, de espera antes de tentar reconectar. Também pode ser enviado por um servidor, imediatamente antes de fechar a conexão do cliente, para reduzir sua carga quando muitos clientes estiverem conectados.

O id associa um identificador ao evento atual. Ao reconectar, o cliente transmitirá ao servidor o último id visto, usando o Last-Event-ID como cabeçalho HTTP. Isso permite que o fluxo seja retomado a partir do ponto correto.

writeHead e flushHeaders são ambos os métodos express usados no nodejs​ você não precisa usar um desses dois para implementar corretamente SSE.

writeHead()é uma alternativa preferencial ao uso do setHeader.

flushHeaders() é usado quando você precisa ignorar o buffer dos cabeçalhos de solicitação. Não é necessário para SSE funcionar corretamente. Portanto, usá-lo será baseado em suas necessidades específicas de comunicação de aplicativos, não nas necessidades do SSE.

Especificar explicitamente um statusCode é baseado em seu uso, mas não é necessário (a menos que você use response.writeHead()). Por exemplo, você pode usar um código de resposta para instruir um cliente a parar de se reconectar usando um código de resposta HTTP 204 Sem conteúdo.

writeHead ou flushHeaders não são usados ​​para manter a conexão aberta. A conexão permanecerá aberta por padrão até que o cliente se desconecte ou você a feche no servidor com response.end() ou response.send() que irá chamar end. A exceção a isso é o tempo limite padrão do HTTP.

Por fim, o servidor pode interromper completamente o mecanismo de reconexão automática retornando uma resposta HTTP 204 Sem conteúdo > https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204

A API Server-Sent Events (Eventsource) está em camadas em HTTP a qual tem um tempo limite.
Quase sempre 30 ou 60 segundos.
Então sua conexão irá cair!

Websocket esta na camada TCP seu tempo limite é mais de 2 horas!

Evitando o tempo limite do servidor em node.js

O que tudo isso faz? Quando um usuário se cadastra no site, o mongo lança um evento, e o express passa pra gente e o vemos no navegador! Um exemplo simples.

Para entender melhor o app completo esta aqui >
https://github.com/Uriel29/SSEServerSentEvents
só dar npm i para instalar as dependências!
npm run dev para rodar
Se não rodar instale o nodemom

Aqui o vídeo e mais um pouco de explicações:

Para se aprofundar:
https://germano.dev/sse-websockets/
https://dev.to/4shub/building-with-server-sent-events-13j
https://medium.com/conectric-networks/a-look-at-server-sent-events-54a77f8d6ff7
https://ably.com/topic/server-sent-events
https://www.digitalocean.com/community/tutorials/nodejs-server-sent-events-build-realtime-app
https://moinism.medium.com/using-nodejs-for-uni-directional-event-streaming-sse-c80538e6e82e
https://developer.mozilla.org/pt-BR/docs/Web/API/Server-sent_events/Using_server-sent_events
https://www.ibm.com/docs/pt-br/was-liberty/base?topic=liberty-starting-server-sent-events
https://dev.to/tqbit/how-to-use-nodejs-for-server-sent-events-sse-5ggj

Espero que isso te ajude! Abraços

Top comments (2)

Collapse
 
alvescaio profile image
Caio Alves

Muito bom. Ótima leitura.

Collapse
 
eduardojm profile image
Eduardo Oliveira

Texto muito bom =)