Pensar en Quarkus te lleva a pensar en rapidez, velocidad, en un ¡ya! pero… ¿lo piensas de manera reactiva o secuencial? Secuencial es pensar de la forma que siempre nos enseñan, y reactiva es como acabamos terminando aprendiendo (queriendo o a la fuerza). Con Mutiny, las operaciones en Quarkus empiezan a sonar de forma reactiva para fluir en paralelo, utilizar varios hilos…

En este post navegaremos en los primeros pasos de Mutiny en Quarkus, un framework encargado de hacernos la vida más sencilla en un mundo reactivo y complicado de entender al principio. Iremos desde qué son los Uni y los Multi, a cómo transformar datos o manejar los errores. Te doy la bienvenida al mundo reactivo en Quarkus.

¿Por qué usar Mutiny?

1 Queremos (necesitamos) ir a la reactividad

Lo más importante es saber qué necesitamos, por encima del qué queremos. Podemos querer un proyecto basado en reactividad cuando realmente no lo necesitamos y sería como tener un motor de un Ferrari en un Seat 600.

Por lo tanto, si realmente necesitamos la programación reactiva en nuestro proyecto Quarkus, tenemos que dar ese salto a la reactividad.

2 Una vez decidido: ¿por qué Mutiny?

3 Conceptos claves: Uni y Multi

Mutiny tiene dos conceptos claves y sobre los que gira todo:

Teniendo claro qué es cada uno, ¿cómo trabajamos con ellos? ¿Qué operaciones podemos realizar?

Lo más sencillo será tirar de un ejemplo:

Uni.createFrom().item("Hola")
    .onItem()
    .transform(s -> s + " mundo")
    .onItem()
    .invoke(s->System.out.println("Emitido"+s))
.onFailure().recoverWithItem("Error por aquí");

En ese código ocurre lo siguiente:

Por lo tanto, los operadores más comunes en Mutiny son:

Uni.combine().all()unis(...).combinedWith(...)

Otros operadores que utilizamos en muchos casos con Multi son los conocidos ya en Java como el merge(). concatenate(), select()... y muchos más que podemos encontrar aquí.

4 Preparar nuestro proyecto Quarkus para Mutiny

Una vez decidido que sí necesitamos Mutiny y la reactividad en nuestro proyecto Quarkus, iremos paso a paso (desde la creación del proyecto):

  1. Generamos nuestro proyecto Quarkus, ya sea vía code.quarkus.io o mvn.
mvn io.quarkus.platform:quarkus-maven-plugin:create \
    -DprojectGroupId=com.example \
    -DprojectArtifactId=mutiny-demo \
    -DclassName="com.example.GreetingResource" \
    -Dpath="/hello"
  1. Si no está la dependencia de Mutiny, la añadimos manualmente en nuestro pom:
<dependency>
  <groupId>io.quarkus</groupId>
 <artifactId>quarkus-mutiny</artifactId>
</dependency>

O vía mvn:

mvn quarkus:add-extension -Dextensions=mutiny
  1. Una vez generado el proyecto, vamos a crear un endpoint muy básico que devuelva un saludo, como se debe hacer cuando vemos a alguien:
package com.example;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import io.smallrye.mutiny.Uni;

@Path("/hello")
public class GreetingResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Uni<String> hello() {
        return Uni.createFrom().item("Hola desde Quarkus + Mutiny")
                  .onItem().transform(s -> s + " — vigente a " + System.currentTimeMillis());
    }
}

Y así, ¡acabamos de hacer nuestro primer endpoint de Mutiny con Quarkus!

5 Uso de Multi para streams y listas

En nuestro primer endpoint nos basamos únicamente en los Uni así que, ahora, vamos a probar el otro elemento clave: Multi. Como recordatorio, utilizamos Multi cuando esperamos múltiples resultados (como streams o listas).

Si tuviésemos este endpoint llamado words:

@Path("/words")
public class WordsResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Multi<String> words() {
        return Multi.createFrom().items("uno", "dos", "tres")
            .onItem().transform(String::toUpperCase);
    }
}

Cuando hacemos la petición (localhost:8080/words), Quarkus nos va a enviar como stream JSON (si nuestro cliente/servidor lo admiten). También puedes combinar Multi con Uni para transformar cada elemento de nuestro Multi en uno individual Uni:

Multi.createFrom().items("a", "b", "c")
     .onItem().transformToUni(s -> Uni.createFrom().item(s + "_x"))
     .concatenate();

Otro ejemplo que nos podemos encontrar con los Multis es la combinación entre ellos mismos, es decir, ¡todo al Multi!

public Uni<String> combined() {
    Uni<String> u1 = Uni.createFrom().item("A").onItem().delayIt().by(Duration.ofMillis(100));
    Uni<String> u2 = Uni.createFrom().item("B").onItem().delayIt().by(Duration.ofMillis(200));

    return Uni.combine().all().unis(u1, u2)
        .combinedWith(list -> {
            String s1 = (String) list.get(0);
            String s2 = (String) list.get(1);
            return s1 + "-" + s2;
        });
}

En este ejemplo, combined() espera que ambas operaciones terminen y luego las combina. No devuelve el resultado hasta que ambas concluyan.

6 Errores comunes y cómo evitarlos

En Quarkus, si tenemos un endpoint definido que devuelve un Uni (por ejemplo, el GET que ponemos en los ejemplos), Quarkus se encarga de suscribirse de manera automática. Es decir, cuando el Uni emite un valor, Quarkus lo transforma en una respuesta HTTP (por ejemplo, un 200 OK con el contenido del Uni). No uses subscribe() manual fuera de contextos de prueba.

No usamos llamadas bloqueantes dentro de nuestras aplicaciones (Thread.sleep, acceso a base de datos bloqueante…). Si necesitamos hacer algo bloqueante, la mejor forma es convertirlo en Uni ejecutado en otro hilo o usar .runSubscriptionOn(...).

Debemos evitar la mutación de listas o variables externas dentro de operadores, ya que puede producir resultados inesperados en modo reactivo.

Diseñar siempre nuestras operaciones desde un principio con Uni y Multi para que todas devuelvan este tipo o trabajen directamente con ellas.

Mutiny tiene gran soporte para test, por lo tanto, usemoslos.

Conclusión

Ya tenemos nuestros primeros cimientos para dar otro paso en la programación reactiva (como ya vimos en otros post enfocados en RxJava) pero esta vez con Quarkus.

Hemos conocido los Multi y los Uni, nuestros nuevos amigos, y hemos visto cómo combinarlos en diferentes operaciones. Si este ha sido tu primer contacto, posiblemente hayas sentido más el salto, ya que nos obliga a cambiar la forma de pensar que teníamos hasta ahora.

Sin embargo, una de las cosas más importantes que debemos hacer es probar y fallar, fallar y volver a fallar. Así conseguiremos aprender más sobre la programación reactiva y que deje de ser una desconocida. ¡Te leo en comentarios!

Referencias

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete