Server-Sent Events (SSE) e como implementar
Tags: sse, backend, realtime, go
Published: 2023-04-08 00:54:23

Server-Sent Events (SSE) é uma tecnologia que permite a comunicação unidirecional entre o servidor e o cliente em tempo real. SSE é uma alternativa aos WebSockets que oferece várias vantagens, como menor sobrecarga de rede e menor uso de recursos. Neste tutorial, iremos aprender como implementar SSE em Go e compará-lo com WebSockets.

## O que é SSE?
SSE é um protocolo de comunicação baseado em HTTP que permite que o servidor envie eventos para o cliente. O cliente se inscreve em um fluxo SSE, que é uma conexão persistente entre o cliente e o servidor. O servidor envia eventos em formato de texto para o cliente, que é capaz de processá-los em tempo real. Os eventos podem ser nomeados e o payload pode ser em formato de texto ou JSON.

## Como implementar?

O exemplo a seguir mostra como implementar SSE utilizando Golang, mas não se apegue a linguagem pois pode ser qualquer outra, basta definir os Headers corretos que você será capaz reproduzir.

### Back-end (Golang)

```go
package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "index.html")
	})

	http.HandleFunc("/sse", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		w.Header().Set("Cache-Control", "no-cache")
		w.Header().Set("Connection", "keep-alive")

		messageCounter := 0

		for {
			someMessage := fmt.Sprintf("Message: %v", messageCounter)

			eventName := "event: myCustomEvent\n"
			payloadMessage := fmt.Sprintf("data: %s\n\n", someMessage)

			w.Write([]byte(eventName))
			w.Write([]byte(payloadMessage))

			time.Sleep(1 * time.Second)

			w.(http.Flusher).Flush()

			messageCounter++
		}
	})

	http.ListenAndServe(":3333", nil)
}
```

A rota `/sse` define os cabeçalhos necessários para enviar eventos SSE. O cabeçalho `Content-Type` é definido como `text/event-stream`, `Cache-Control` é definido como `no-cache` para evitar o armazenamento em cache e `Connection` é definido como `keep-alive` para manter a conexão aberta. Em seguida, o servidor inicia um loop infinito para enviar eventos SSE. O evento é opcional e pode ser usado para especificar o tipo de evento que está sendo enviado. O payload contém os dados que estão sendo enviados.

> O uso do `\n` é obrigatório, você pode observar que uso ele ao definir o nome do evento e payload da mensagem. O SSE vai conseguir interpretar quando essas informações foram definidas após essa quebra de linha, por fim, o `\n` adicional informa que acabou aquele evento.

Exemplo de como vai ficar no final após a interpretação:

```text
event: myCustomEvent
data: Message: 1

event: myCustomEvent
data: Message: 2
```


### Front-end (HTML + JavaScript)

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SSE</title>
</head>
<body>
  <h1>SSE - Real Time Messages</h1>

  <ul id="messages"></ul>

  <script>
    const eventSource = new EventSource('/sse');

    eventSource.addEventListener('myCustomEvent', event => {
      const messagesElement = document.getElementById('messages');
      const li = document.createElement('li');

      li.innerText = event.data;

      messagesElement.appendChild(li);

      console.log(event);
    });
  </script>
</body>
</html>
```

O código acima é muito simples, basicamente vamos estar recebendo as mensagens que o nosso Back-end vai mandar, e vamos adicionar elas em tela, somente isso.

A instância do `EventSource`, recebe a URL do endpoint SSE que o servidor está ouvindo (**/sse** neste caso). Isso estabelece uma conexão persistente entre o cliente e o servidor, permitindo que o servidor envie atualizações em tempo real para o cliente.

Em seguida, é adicionado um ouvinte de eventos para o evento personalizado `myCustomEvent`, que é enviado pelo servidor. Quando esse evento é recebido, uma nova linha de lista `li` é criada, contendo o texto da mensagem que foi enviada pelo servidor. A linha de lista é então adicionada ao elemento `ul` com o id `messages`, que é onde as mensagens são exibidas na página.


![Front-end Preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5qkgynw1wnv1ch0lav75.png)


![Front-end Network Tab](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dp7gase9q9xdk2b78ngt.png)

## Vantagens e Desvantagens do SSE

SSE tem várias vantagens em relação aos WebSockets. A primeira é que ele é baseado em HTTP e usa a porta HTTP padrão (80), o que significa que ele pode passar por firewalls e proxies sem problemas. Além disso, o SSE requer menos sobrecarga de rede e recursos do servidor do que o WebSocket, porque apenas o servidor pode enviar dados. Isso significa que o servidor não precisa lidar com tantas conexões de saída e, portanto, pode lidar com mais conexões de entrada. Finalmente, o SSE é fácil de implementar, pois é baseado em HTTP e usa o mesmo modelo de programação.

No entanto, o SSE também tem algumas desvantagens. A primeira é que ele só suporta comunicação unidirecional (do servidor para o cliente), o que significa que o cliente não pode enviar dados para o servidor. Além disso, o SSE não é suportado por todos os navegadores e plataformas, o que significa que você precisará testar se o SSE funciona em todos os navegadores e plataformas que você deseja suportar.

## Comparação SSE vs WebSockets

Embora SSE e WebSockets possam parecer semelhantes, eles têm algumas diferenças importantes. A principal diferença é que o WebSocket é bidirecional, o que significa que tanto o cliente quanto o servidor podem enviar dados. Isso torna o WebSocket mais adequado para aplicativos em tempo real que exigem interação bidirecional entre o cliente e o servidor, como jogos online ou aplicativos de bate-papo. O SSE, por outro lado, é mais adequado para aplicativos unidirecionais, como feeds de notícias ou atualizações de preços.

Além disso, o WebSocket usa um protocolo de comunicação personalizado que é implementado em um nível mais baixo do que o HTTP. Isso significa que o WebSocket é capaz de suportar recursos avançados, como multiplexação de canais, compressão de dados e autenticação do lado do servidor. O SSE, por outro lado, é implementado como um recurso do HTTP e, portanto, não pode usar esses recursos avançados.