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 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.
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.
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.
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.
(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).
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.
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.
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.
Top comments (0)