Realtime sin websockets

Cuando nos llega un requerimiento de aplicaciones en tiempo real y este requerimiento viene despues de Junio del 2018, la primera solucion que te vendra a la mente, sera usar websockets (creo) porque es una de las formas en las que podemos obtener actualizaciones de informacion desde el servidor, sin embargo, muchas empresas sobre todo las mas actualizadas (bancos, aseguradoras e instituciones financieras), debido a sus restricciones de red no siempre permiten usar protocolos tales como websockets y muchos desarrolladores piensan que la unica alternativa a websockets es polling o long-polling (hacer requests cada 5 o 3 segundos, o extender el request lo mas posible hasta recibir una respuesta) y usar una libreria como cometd para ayudar con eso (adivinas, tampoco me gusta cometd, de hecho soy uno de esos desarrolladores que no le gustan muchas cosas :P).

Existe una tercera opcion llamada SSE esta opcion es tal vez la menos usada, no se bien si solo es mi impresion o es verdad, no cuento con estadisticas para soportar esta afirmacion, lo que si es seguro es que no tenemos problemas de protocolos, porque es simplemente un http request pero con esteroides, al igual que long-polling no cerramos la conexion, pero a diferencia de long-polling si recibimos informacion tampoco cerramos la conexion, esta es una enorme diferencia y si puedes mantener el request abierto por 10 minutos solo ocupas uno.

Si eres afortunado y estas en una organizacion que puede entregar http2 y sus clientes usan navegadores que lo soporten, los beneficios son todavia mayores, ya que el canal se optimiza mucho mas y el numero de conexiones que se crean se reducen drasticamente. Ahora bien SSE tampoco es la ultima tecnologia del siglo, tambien tiene rato, y aunque no es soportado por todos los navegadores (IE como casi siempre) existe un polyfill que lo integra para IE 11, ahora bien, esto es tan sencillo que crear tu propio cliente SSE no es complicado, pero para que hacer eso, usemos el del navegador.

El server

En este post vamos a iniciar creando el servidor Web SSE, en el siguiente post crearemos el cliente, y luego mandaremos un mensaje de eco, y veremos si nos responde adecuadamente :D.

Creamos un nuevo proyecto kotlin con Intellij community edition y gradle y vamos a agregar las siguientes dependencias al archivo build.gradle como ya sabemos.

    compile 'org.slf4j:slf4j-api:1.7.26'
    compile 'org.slf4j:slf4j-simple:1.7.26'
    compile 'io.undertow:undertow-core:2.0.20.Final'

Exacto lo vamos a hacer con Undertow, un servidor que nos ofrece interesentes capacidades asynchronas no bloqueantes. Y ademas tiene un handler para SSE, lo cual nos facilitara el trabajo, creamos nuestro Main.kt file:

import io.undertow.Handlers
import io.undertow.Undertow
import io.undertow.server.handlers.sse.ServerSentEventHandler
import io.undertow.util.HttpString

val messageSender = { sseHandler: ServerSentEventHandler ->
    { message: String ->
        println(message)
        for (h in sseHandler.connections) {
            h.responseHeaders.put(HttpString("Access-Control-Allow-Origin"), "*")
            h.responseHeaders.put(HttpString("Access-Control-Allow-Methods"), "POST, GET, OPTIONS")
            h.responseHeaders.put(HttpString("Access-Control-Max-Age"), 86400)
            h.responseHeaders.put(
                HttpString("Access-Control-Allow-Headers"),
                "Origin, Host, Referer, User-Agent, Accept, Accept-Encoding, Accept-Language, Cache-Control, X-PINGOTHER, Content-Type"
            )
            h.send(message)
        }
    }
}

fun main() {
    val sseHandler = Handlers.serverSentEvents()
    val server = Undertow.builder()

    server.setIoThreads(2)
    server.setWorkerThreads(2)
    server.addHttpListener(7080, "localhost")
    server.setHandler(Handlers.path()
        .addPrefixPath("/sse", sseHandler)
        .addPrefixPath("/")  { exchange ->
            exchange.responseHeaders.put(HttpString("Access-Control-Allow-Origin"),"*")
            exchange.responseHeaders.put(HttpString("Access-Control-Allow-Methods"),"POST, GET, OPTIONS")
            exchange.responseHeaders.put(HttpString("Access-Control-Max-Age"),86400)
            exchange.responseHeaders.put(HttpString("Access-Control-Allow-Headers"),"X-PINGOTHER, Content-Type")
            exchange.responseSender.send("Hello World")
        }

    )

    val sendMessage = messageSender(sseHandler)

    server.build().start()
   
}

El codigo es bastante simple, agregamos dos paths "/" y "/sse" el primero es para configurar CORS que sera la primera version de este server y el segundo es para escuchar peticiones para SSE, la funcion messageSender nos permite enviar mensajes en cualquier momento que queramos, que en este momento no estamos enviando nada, pero cuando tengamos el html podremos enviar ecos :D, bueno, aqui como puedes ver ademas de todo Undertow casi se parece a Express de nodejs que es bastante simple y poderoso, nada nos limita para crear servicios REST con Undertow sin tanta paja como con Spring boot (si tampoco me gusta spring, :P, pero no significa que a ti no te pueda gustar, para gustos hay moles).

En el siguiente post crearemos el cliente con React para el eco usando Parcel. Luego veremos que mas hacemos.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de Nopalin

Como es diferente de websockets?

Websocket es un protocolo de comunicación por encima de HTTP, que a su vez funciona através de TCP. Hablar de websocket no es otra cosa que una conexión TCP persistente que se mantendrá abierta mientras dure la sesión.

Esta libreria no la conozco, pero podrias explicar un poco más a fondo como es que funciona y como es mas segura que websocket?

Saludos

PD websocket puede funcionar sobre HTTPS.