Programación reactiva con Vert.x

¿Quién soy?

{
    "name":"Fran García",
    "email":"fgarciarico@gmail.com",
    "twitter":"@frangarcia",
    "linkedin":"https://www.linkedin.com/in/frangarcia/",
    "position":"Ingeniero de Software Senior en Wayin",
    "description":"Desde 2014 con Vert.x"
}

Contexto actual


Fuente: https://www.ericsson.com

Solución

Aprovechemos mejor los recursos.

Programación reactiva

¿Qué es la programación reactiva?

Se refiere a un paradigma de programación enfocado al tratamiento de flujos de datos de forma asíncrona tal y como se recoge en el Reactive Manifesto http://www.reactivemanifesto.org

Principios del Reactive Manifesto

  • Responsivos
  • Resilientes
  • Elásticos
  • Orientados a Mensajes

Responsivos

  • Responder de manera oportuna
  • Detectar problemas rápidamente
  • Tiempos de respuesta rápidos

Resilientes

  • Responder correctamente a fallos
  • Replicación
  • Aislamiento

Elásticos

  • Siempre responsivo ante diferentes niveles de carga
  • Reaccionar a los cambios
  • Evitar cuellos de botella

Orientados a Mensajes

  • Intercambio de mensajes asíncronos
  • Bajo acoplamiento
  • Aislamiento

Arquitectura del Manifiesto de Sistemas Reactivos


Fuente: http://www.reactivemanifesto.org

Hollywood Principle


Fuente: https://dzone.com/articles/the-hollywood-principle

¿Qué es Vert.x?

Eclipse Vert.x (http://vertx.io) es un conjunto de utilidades (no un framework) basado en 2 conceptos

  • Diseño dirigido por los eventos
  • Es NO bloqueante

Características de Vert.x

  • Propósito general
  • Políglota
    • Java
    • Javascript
    • Groovy
    • Ruby
    • Ceylon
    • Scala
    • Kotlin
  • Escalabilidad
  • No dogmático

Características de Vert.x

  • Muy ligero
  • Rápido
  • No es un servidor de aplicaciones
  • Modular
  • Sencillo pero no simplista
  • Ideal para microservicios
  • Buenísima documentación

¿Quién utiliza Vert.x?

Companies

Instalación de Vert.x

						curl -s "https://get.sdkman.io" | bash

source "$HOME/.sdkman/bin/sdkman-init.sh"

sdk install vertx
					

Vert.x core

  • Clientes y servidores TCP
  • Clientes y servidores HTTP
  • Websockets
  • Event bus
  • Datos compartidos
  • Acciones periódicas
  • Desplegar verticles
  • Clientes DNS
  • Acceso al sistema de ficheros
  • Alta disponibilidad
  • Clustering

Integración

  • MySQL
  • PostgreSQL
  • Elasticsearch
  • Redis
  • Kafka

¿Qué es el event loop?

  • ¿Tenemos algún evento que procesar?
  • Lo proceso y llamo a los handlers necesarios
  • Y así constantemente
  • Tantos hilos de event loop como número de núcleos * 2

Event loop

Event loop Fuente: http://escoffier.me/vertx-kubernetes/

Event loop


Fuente: http://www.vertx.io

¿Igual que Node.js?

Node.js utiliza un único event loop mientras que Vert.x utiliza un event loop por cada core multiplicado por dos. Esto se conoce como Multi-Reactor Pattern.

La regla de oro

!!No bloquees el event loop!!

Ejemplos de bloquear el event loop

  • Thread.sleep()
  • Costosas operaciones con bases de datos
  • Cálculos complejos que tarden mucho

Ejecutar código bloqueante

vertx.executeBlocking({ future ->
  // Llama a métodos bloqueantes
  def result = someAPI.blockingMethod("hello")
  future.complete(result)
}, { res ->
  println("The result is: ${res.result()}")
})

Talk is cheap, show me the code

Talk is cheap, show me the code Fuente: Recruiting daily

Ejemplo: Hello world!

vertx run s01e01.groovy

import io.vertx.core.http.HttpServer

HttpServer httpServer = vertx.createHttpServer()

//Responder a cada petición con un Hello World!
httpServer.requestHandler({ request ->
  // Este handler será llamado con una petición al servidor
  request.response().end("hello world!")
})
httpServer.listen(8080)

Ejemplo: Devolviendo Json

vertx run s01e02.groovy

import io.vertx.core.http.HttpServer

HttpServer httpServer = vertx.createHttpServer()

//Responder a cada petición con un Hello World!
httpServer.requestHandler({ request ->
  String json = """{"foo":"bar"}"""
  request.response()
  .putHeader("Content-Type", "application/json")
  .putHeader("Content-Length",json.size().toString())
  .write(json)
  .end()
})
httpServer.listen(8080)

Ejemplo: Ejecuciones periódicas

vertx run s01e03.groovy

vertx.setPeriodic(1000, {id ->
  println("Temporizador ejecutado! ${id}")
})

Ejemplo: Temporizadores

vertx run s01e04.groovy

Closure handler
handler = { id ->
  println("Temporizador ejecutado! ${id}")
  vertx.setTimer(1000*(id+1), handler)
}

vertx.setTimer(1000, handler)

Verticles

  • Suponen el corazón de Vert.x
  • Aunque no estás obligado a utilizarlos
  • Una aplicación consiste habitualmente en muchos verticles ejecutándose simultáneamente en una instancia de Vert.x

Escribiendo verticles

Desplegando verticles

vertx run s01e05.groovy

vertx.deployVerticle("HelloWorldHttpVerticle.groovy")
vertx.deployVerticle("s01e06.groovy")

Desplegando verticles: HelloWorldHttpVerticle.groovy

import io.vertx.core.AbstractVerticle

public class HelloWorldHttpVerticle extends AbstractVerticle {

  public void start() {
    println("Starting HelloWorldHttpVerticle")
  }

  public void stop() {
    println("Stopping HelloWorldHttpVerticle")
  }
}

Desplegando verticles: s01e06.groovy

void vertxStart() {
  println "starting s01e06"
}

void vertxStop() {
  println "stopping s01e06"
}

Desplegando verticles de forma asíncrona

vertx run s01e07.groovy

import io.vertx.core.Future

void vertxStart(Future future) {
  vertx.deployVerticle("s01e06.groovy", { res ->
    if (res.succeeded()) {
      println "s01e06 started successfully asynchronously"
      future.complete()
    } else {
      println "error starting s01e06 asynchronously"
      future.fail("Error starting s01e06")
    }
  })
}

void vertxStop(Future future) {
  println "stopping s01e06"
  future.complete()
}

Desplegando verticles de forma asíncrona

vertx run HelloWorldHttpVerticleAsync.groovy

import io.vertx.core.AbstractVerticle
import io.vertx.core.Future

public class HelloWorldHttpVerticleAsync extends AbstractVerticle {

  public void start(Future future) {
    println "starting"
    vertx.deployVerticle("s01e06.groovy", { res ->
      if (res.succeeded()) {
        println "s01e06 started successfully asynchronously"
        future.complete()
      } else {
        println "s01e06 not started asynchronously due to an error"
        future.fail()
      }
    })
  }

  public void stop(Future future) {
    println("stopping")
    future.complete()
  }
}

Desplegando verticles con varias instancias

vertx run s01e08.groovy

vertx.deployVerticle("HelloWorldHttpVerticle.groovy", [instances:5])

Tipos de verticles

  • Standard
  • Worker
  • Multi-Threaded

Standard verticles

  • Se asigna un event loop cuando se crean
  • Vert.x garantiza que cualquier código de estos verticles siempre se ejecutará en el mismo event loop
  • De esta forma, escribe tu aplicación como si fuera monohilo que Vert.x ya se encargará de hacerlo multihilo y de la escalabilidad

Worker verticles

  • No utilizan el event loop
  • Utilizan un thread del pool de threads de Vert.x
  • Están diseñados para ejecutar código bloqueante
def options = [
  worker:true
]
vertx.deployVerticle("MyFirstVerticle", options)

Multi-thread worker verticles

  • Como un worker verticle pero que puede ser ejecutado en multiples threads

Event bus

  • Es el sistema nervioso de Vert.x
  • Un unico event bus para cualquier verticle
  • Es la mejor forma de comunicar verticles en Vert.x

¿Cómo funciona el Event bus?

  • Mandamos mensaje a una dirección, e.g. europe.news.feed
  • Los mensajes son simples cadenas de texto
  • Los handlers reciben los mensajes, pero antes habrá que registrarlos
  • Varios handlers pueden estar suscritos en una misma dirección
  • Un mismo handler puede estar suscritos en varias direcciones

Tipos de subscripción

  • Publish/Subscribe
  • Punto a punto

Tipos de subscripción: Publish/Subscribe

Todos los handlers suscritos a una dirección recibirán el mensaje.

Tipos de suscripción: Punto a punto

Vert.x se encargará de que sólo un handler reciba el mensaje. Se puede especificar incluso un reply handler.

Tipos de mensajes en el event bus

Normalmente se utiliza Json, aunque no estamos forzados a ello.

Registrando handlers

def eb = vertx.eventBus()

eb.consumer("news.uk.sport", { message ->
  println("I have received a message: ${message.body()}")
})

Ejemplo: Publish/Subscribe

vertx run PublishingMessage.groovy

import io.vertx.core.http.HttpServer

def eventBus = vertx.eventBus()

eventBus.consumer("news.uk.sport", { message ->
  println("EH1: I have received a message: ${message.body()}")
})

eventBus.consumer("news.uk.sport", { message ->
  println("EH2: I have received a message: ${message.body()}")
})

HttpServer httpServer = vertx.createHttpServer()

httpServer.requestHandler({ request ->
  eventBus.publish("news.uk.sport", "Yay! Someone kicked a ball")
  request.response().end("event sent")
})
httpServer.listen(8080)

Ejemplo: Punto a punto

vertx run PointToPointExample.groovy

import io.vertx.core.http.HttpServer

def eventBus = vertx.eventBus()

eventBus.consumer("news.uk.sport", { message ->
  println("EH1: I have received a message: ${message.body()}")
})

eventBus.consumer("news.uk.sport", { message ->
  println("EH2: I have received a message: ${message.body()}")
})

HttpServer httpServer = vertx.createHttpServer()

httpServer.requestHandler({ request ->
  eventBus.send("news.uk.sport", "Yay! Someone kicked a ball")
  request.response().end("event sent to one consumer")
})
httpServer.listen(8080)

Ejemplo: Respondiendo al evento

vertx run EventReplyExample.groovy

import io.vertx.core.http.HttpServer

def eventBus = vertx.eventBus()
eventBus.consumer("news.uk.sport", { message ->
  println("I have received a message: ${message.body()}")
  message.reply("Event received, thanks!")
})
      
HttpServer httpServer = vertx.createHttpServer()
httpServer.requestHandler({ request ->
  eventBus.send("news.uk.sport", "Yay! Someone kicked a ball", { ar ->
    if (ar.succeeded()) {
      request.response().end("This is what we received: \"${ar.result().body()}\"")
    } else {
      request.response().end("There was an error")
    }
  })
})
httpServer.listen(8080)

Ejercicio

Si sólo conocieramos Eclipse Vert.x, ¿como diseñaríamos la arquitectura de un servicio para registrar usuarios que necesita mandar un mail de confirmación a través de un servicio externo y al mismo tiempo el nuevo usuario debe ser añadido a otro servicio externo?

Demo: Juego

https://bit.ly/3n7Izg5
Game QR

Más información

Thanks
Fuente: https://velvetyblog.com

¿Preguntas?