SSE con Golang y Nats
Tags: go, distributedsystems, webdev, eventdriven
Published: 2022-09-11 16:12:42

Arquitectura orientada a eventos con dos servicios comunicándose mediante Nats y enviando eventos con Golang.

## Qué es un _Evento_?
 
Para no ponernos muy técnicos innecesariamente, lo definimos como un suceso que queremos comunicarlo a otro servicio. Entonces es "algo" y vamos a enviarle esa información a alguien más.

## Entonces, qué es **SSE**?

Server side events es un protocolo que envía mensajes donde un servidor es la fuente (eventsource), que funciona perfecto para las aplicaciones que quieran mostrar datos de forma continua (streams). Además, la información que vamos a mostrar depende de los eventos que vayan ocurriendo, es decir que al momento de la ocurrencia, el cliente (con un poco de suerte) vamos a poder ver la información enviada desde el servidor sin tener que refrescar la pagina.

Entonces vamos a conectarnos desde un browser a una URL y recibir mensajes sin necesidad de hacer nada más. Esto es conocido como XHR streaming, similar a las conocidas peticiones AJAX pero con un canal establecido y solo recibiendo eventos desde el servidor.

## Tecnologías utilizadas

Para esta demo, elegimos Golang, sin frameworks y [nats](https://nats.io) que es, según ellos, un software de conectividad cloud native y nosotros lo vamos a usar como un pub-sub, alguien publica un mensaje y otro(s) se subscribe al tópico para tener ya disponibles los mensajes. Es por eso que siempre hablamos de _near real-time_ porque no podemos desprendernos de la latencia natural que existe en las comunicaciones, pero vemos que Go y Nats hacen muy bien su trabajo y con tan pocas lineas de código vamos a tener una ejecución rápida y eficiente.
Docker lo vamos a usar para nuestra demo local.

## Nuestro caso de uso

Para el ejemplo, vamos a simular algunos drones están en un campo de batalla enemigo y escanean una zona en particular, obtienen información de las tropas, como cantidad de soldados, nivel de energía y experiencia (skill). Muchos drones en simultaneo van a estar enviando mensajes a través de nats, y el servicio de monitoreo los va a capturar, finalmente, vamos a ver los resultados en una web como streams de datos, cada vez que el monitor reciba un mensaje, lo procesa y lo envía por el canal de XHR streaming.
Recordemos que streaming es una extension de XHR que nos permite tener datos (bytes) disponibles al mismo momento que llegan, independientemente de un buffer que se los contenga. 

Esta capacidad esta disponible en algunos browsers, no todos, por eso websockets sigue siendo muy popular pero de este modo tenemos conexión bidireccional, con el agregado HTTP que no tenemos usando websockets. Es por esto que vamos a tener que, mediante código, avisarle al cliente sí su navegador soporta nuestra operación.

---
Ahora si nos vamos al editor


En primer lugar vamos a definir el mensaje algunas estructuras y funciones.

{% embed https://gist.github.com/tomiok/c865eb4134c7ff78678c289aa4f86953 %}

Tanto Drone como Scan son para agrupar la info leída y las que componen la estructura de _Message_ la vamos a usar para enviar el JSON.

Ahora para conectarnos a nats local y enviar mensajes, es muy fácil y casi imposible equivocarse. Va a correr sobre Docker.

{% embed https://gist.github.com/tomiok/f77b66b27425205c12d58ce27d47e829 %}

Creo que no hace falta explicar mucho, lo unico importante de remarcar es el puntero a la conexion, ya que es una estructura y el subject que lo usamos como nombre del topico donde enviamos los mensajes.

Para ejecutar localmente, el docker compose es mas facil aun.

{% embed https://gist.github.com/tomiok/6e3609741d1fe93c6a9487b467afae1b %}

con un comando simple como `docker-compose up` ya vamos a tener una instancia local.


Con estas pocas lineas ya podemos enviar mensajes, ahora vamos a la otra sección, que es como vamos a recibirlos y mostrarlos.

## servicio de monitoreo

Ya los drones están enviando sus _scans_, así que vamos a subscribirnos al tópico para leerlos.

Para conectarnos usamos lo mismo.
{% embed https://gist.github.com/tomiok/17970df47ffa339da28cbd3000d1f4a6 %}

(ya vamos a usar el canal)

Acá vemos como nats nos da una firma con una _closure_ para que hagamos el trabajo con el mensaje entrante. En nuestro caso se lo pasamos a otro canal (Nats también soporta nativamente la subscripción con canales en Go).

{% embed https://gist.github.com/tomiok/e013647ebc42e40c29bb88d905d10f24 %}


El método `Listen()` lo usamos de pasamanos y formateo, y el `ReadForever()` para que sea capturar todos los mensajes. 
En main vamos a usar una goroutine separada para que readForever cumpla su trabajo.

{% embed https://gist.github.com/tomiok/d4879b621d4a2d654709fa52365e5b81 %}

Ahora pasamos a la última parte, enviar los streams al cliente web, en este caso un navegador.
Vamos a usar la interfaz `Flusher()` de Go que nos permite enviar los datos "en un buffer". Según la documentación, debemos validar en runtime si es posible hacer esta operación porque algunas implementaciones de los servidores web pueden no soportarlo.

Dentro del bucle _infinito_ vamos a estar tomando todos los mensajes (podríamos usar la cláusula `range` también) y dandole un formato mas legible. La otra linea importante es `Flush()`para que _realmente_ el mensaje se termine de enviar.

Por ultimo, no se olviden los headers, importante que el navegador conozca que no tiene que usar cache, mantener la conexión viva y que el content-type va a ser streaming.

{% embed https://gist.github.com/tomiok/2bd4f6e63de3c0e71ccefdf248be8b59 %}

## Lo probamos

Podemos usar cualquier navegador y al tener los dos servicios corriendo, ingresamos a `localhost:3335/listen` (al menos asi lo tengo en mi GitHub y vamos a ver en tiempo real los eventos.

## Conclusión

Como vimos, el patrón SSE es muy interesante y con muchísimos casos de uso aplicables. Con pocas lineas de código, lo hacemos realidad y es a prueba de fallas. No entramos en comparación detallada con otras tecnologías como websockets porque la idea era mostrar una alternativa, aunque siempre se tienen que evaluar ambas.

Espero que les haya gustado, los espero en comentarios y dejo todo el _source_: https://github.com/tomiok/nrt y si te gustó, podes [sponsorearme aca](https://github.com/sponsors/tomiok).