Microservicios reactivos con Vert.x

Hola a todos! En este articulo vamos a hablar sobre mi tema mas favorito – microservicios reactivos con Vert.x. Vert.x es un framework para el desarrollo de aplicación orientada sa eventos que se ejecuta en JVM. Y, que es importante, Vert.x es un “polyglot”. En este articulo usamos solamente Java, sin embargo las aplicaciones pueden ser escritas también en Scala, JS, Python, Ruby, Kotlin, Groovy.

Qué significa “microservicio”?

Microservicos es un estilo de arquitectura de software. En este caso, una applicación consta de una colección de componentes autónomos y pequeños (se llaman “microservicios”). Estos componentes son independientes entre sí y cada uno debe implementar una funcionalidad de negocio individual. Una arquitectura de microservicios es opuesta a la arquitectura mas tradicional de monolith. Vean en este gráfico:

Arquitectura monolítica vs. Arquitectura microservicios (Apiservice.cl)

En comparación con las aplicaciones monolíticas, los microservicios son más fáciles para diseñar, probar, implementar y actualizar. La arquitectura usa “servicios” como componentes lógicos. Este enfoque tiene algunas ventajas:

  • Microservicios están acoplados de forma flexible. Son pequeños y tiene un enfoque claro. Y por eso mas comodos para diseñar y implementar.
  • El servicio es un código base independiente. Entonces podemos usar lenguajes de programación y herramientas que son mas adequadas para cada situación.
  • Los servicios se comunican entre sí mediante API. Es importante, definir el mismo API para communicar.
  • Los servicios tienen un mejor “Return on Investment”. Necesitamos un hardware mas barato para ejecutar servicos, que para ejecturar aplicaciones monolithes.

En otras palabras, esta arquitectura es un intento de desplegar servicios independentes con un contexto definido.

Y por que son reactivos?

La palabra “reactivo” significa otro concepte muy importante de arquitectura de software. Unas sistemas reactivos, en general, son más flexibles, con bajo acoplamiento y escalables. Estas sistemas tienen siguientes caracteristicas (según al Manifiesto de Sistemas Reactivos):

  1. Son responsivas: unas sistemas se enfocan en proveer tiempos de respuesta rápidos y consistentes, estableciendo límites superiores confiables para así proporcionar una calidad de servicio consistente
  2. Son resilientes: unas sistemas permanecen responsivo frente a fallos
  3. Son elásticos: unas sistemas pueden reaccionar a cambios en la frecuencia de peticiones incrementando o reduciendo los recursos asignados para servir dichas peticiones
  4. Son orientados a mensajes: unas sistemas comunican con mensajes. El uso del intercambio de mensajes explícito posibilita la gestión de la carga, la elasticidad, y el control de flujo, gracias al modelado y monitorización de las colas de mensajes en el sistema, y la aplicación de back-pressure cuando sea necesario

Empezamos con Vert.x

Vert.x es una solución obvia para implementar las conceptos antedichos. Vert.x, comparar con Java frameworks clásicos, esta orientada de una arquitectura de microservicios reactivos. La realización de esta arquitectura no es dificíl – Vert.x Core nos propociona todos los componentes necesarios: Verticles, EventBus y messaging. Adicionalmente, añadimos Vert.x Web porque necesitamos Router para implementar HTTP server routing. Podemos usar Vert.x Starter para generar un nuevo proyecto:

Necesitamos creer 2 components: Endpoint y Service. Estan conectado con Event Bus que puede transmitir mensajes en una manera reactiva. Este arquitectura de software se llama Endpoint-Service-Repository.

Endpoint

Endpoint es un componente que conecta una aplicación con el otro mundo 🙂 Normalmente, consiste en Router y AuthManager para asegurar unas rutas (pero no concentramos en una seguridad en este articulo).

private EventBus bus;
private AuthProvider authProvider;
@Override
public void start(Future<Void> startFuture) throws Exception {
    super.start(startFuture);
    Future<Void> initSteps = initAuthProvider().compose(s->initServer());
    initSteps.setHandler(startFuture.completer());
}
private Future<Void> initServer(){
    Future<Void> future = Future.future();
    //inicializamos un serveridor y un router
    HttpServer server = vertx.createHttpServer();
    Router router = Router.router(vertx);
    //obtenemos una referencia de EventBus
    this.bus = vertx.eventBus();
    //assignamos hanlderers en rutas
    router.get("/chat/:id").handler(this::getChats);
    //assignamos un serveridor
    server.requestHandler(router).listen(4567, res->{
        if (res.succeeded()){
            System.out.println("Created a server on port 4567");
        } else {
            System.out.println(res.cause().getLocalizedMessage());
            future.fail(res.cause());
        }
    });
    return future;
}


private void getChats(RoutingContext ctx){
    String chatId = ctx.pathParam("id");
    JsonObject request = new JsonObject();
    request.put("chatId", chatId);
    //enviar request a consumidor
    bus.send("consumer.service.messages.getchat", request, res->{
        if (res.succeeded()){
           ctx.response()
.setStatusCode(200)
.end(Json.encode(res.result().body()));
        } else {
            System.out.println(res.cause().getLocalizedMessage());
            ctx.response().setStatusCode(404).end();
        }
    });
}

Este codigo hace siguientes acciones:

  1. Obtiene el Id desde un parametro de la ruta
  2. Crea un Request. Básicamente eso es un JsonOnject con datos, en nuestro ejemplo – userId
  3. Envia un mensaje a EventBus. Especificamos un destino (address), payload y handler (esto es opcional) para negociar con una respuesta
  4. Envia los datos al usario.

Service

El segundo componente de nuesta arquitectura se llama Service. Service se extiende un AbstractVerticle también. En el metodo start() creamos un consumidor de mensajes con una dirección consumer.service.messages.getchat:

@Override 
public void start() throws Exception { 
    super.start();
    EventBus bus = vertx.eventBus();
    bus.consumer("consumer.service.messages.getchat").handler(msg->{
        JsonObject request = JsonObject.mapFrom(msg.body());
        Chat result = chatDao.find(request.getString("chatId"));
        msg.reply(Json.mapFrom(result));
    });
 }

La conclusión

En este articulo, hemos hablado sobre unos microservicios reactivos y como implementar esta arquitectura de software con Vert.x. Por supuesto, Vert.x es una solución obvia para implementar las conceptos antedichos. Vert.x, comparar con Java frameworks clásicos, esta orientada de una arquitectura de microservicios reactivos.

La bibliografía

  • Cecilio Álvarez Caules. Introducción a Vertx (2016) Arquitectura Java. Leer aqui
  • Estilo de arquitectura de microservicios (2018) Microsoft. Leer aqui
  • Jose Hernández. Vert.x (2017) Leer aqui