Uso de la API de Eclipse Vert.x desde una aplicación Quarkus
Vert.x es un conjunto de herramientas para construir aplicaciones reactivas. Como se describe en la Arquitectura Reactiva de Quarkus, Quarkus utiliza Vert.x por debajo.
Las aplicaciones de Quarkus pueden acceder y utilizar las API de Vert.x.
Esta guía presenta cómo se puede construir una aplicación Quarkus utilizando:
-
la instancia gestionada de Vert.x
-
el bus de eventos Vert.x
-
el cliente web Vert.x
Es una guía introductoria. La guía de referencia Vert.x cubre características más avanzadas como los vértices y los transportes nativos.
Arquitectura
Vamos a construir una aplicación sencilla que exponga cuatro endpoints HTTP:
-
/vertx/loremdevuelve el contenido de un pequeño archivo -
/vertx/bookdevuelve el contenido de un archivo grande (un libro) -
/vertx/helloutiliza el bus de eventos de Vert.x para producir la respuesta -
/vertx/webutiliza el cliente web Vert.x para recuperar datos de Wikipedia
Solución
Le recomendamos que siga las instrucciones de las secciones siguientes y cree la aplicación paso a paso. No obstante, puede ir directamente al ejemplo completado.
Clone el repositorio Git: git clone https://github.com/quarkusio/quarkus-quickstarts.git o descargue un archivo.
La solución se encuentra en el directorio vertx-quickstart.
|
Mutiny
Esta guía utiliza la API Mutiny. Si no está familiarizado con Mutiny, consulte Mutiny - una biblioteca de programación intuitiva y reactiva. |
Arranque de la aplicación
Haga clic en este enlace para configurar su aplicación. Se han seleccionado algunas extensiones:
-
rest-jackson, que también traerest. Vamos a utilizarlo para exponer nuestros endpoints HTTP. -
vertx, que proporciona acceso a la instancia gestionada de Vert.x subyacente
Haga clic en el botón Generate your application, descargue el archivo zip y descomprímalo.
A continuación, abra el proyecto en su IDE favorito.
Si abre el archivo de construcción generado, podrá ver las extensiones seleccionadas:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx</artifactId>
</dependency>
implementation("io.quarkus:quarkus-rest-jackson")
implementation("io.quarkus:quarkus-vertx")
Mientras está en su archivo build, añada la siguiente dependencia:
<dependency>
<groupId>io.smallrye.reactive</groupId>
<artifactId>smallrye-mutiny-vertx-web-client</artifactId>
</dependency>
implementation("io.smallrye.reactive:smallrye-mutiny-vertx-web-client")
Esta dependencia proporciona el cliente web Vert.x, que utilizaremos para implementar el endpoint /web.
Acceso a la instancia gestionada de Vert.x
Cree el archivo src/main/java/org/acme/VertxResource.java.
Contendrá nuestros endpoints HTTP.
En este archivo, copie el siguiente código:
package org.acme;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.Vertx;
import java.nio.charset.StandardCharsets;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@Path("/vertx") (1)
public class VertxResource {
private final Vertx vertx;
@Inject (2)
public VertxResource(Vertx vertx) { (3)
this.vertx = vertx; (4)
}
}
| 1 | Declara la ruta HTTP raíz. |
| 2 | Utilizamos la inyección del constructor para recibir la instancia gestionada de Vert.x. La inyección de campos también funciona. |
| 3 | Recibe la instancia de Vert.x como parámetro del constructor |
| 4 | Almacena la instancia gestionada de Vert.x en un campo. |
Con esto, podemos empezar a implementar los endpoints.
Uso de la API central de Vert.x
La instancia Vert.x inyectada proporciona un conjunto de APIs que puede utilizar. La que vamos a utilizar en esta sección es el Sistema de Archivos Vert.x. Proporciona una API no bloqueante para acceder a los archivos.
En el directorio src/main/resources, cree un archivo lorem.txt con el siguiente contenido:
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
A continuación, en el archivo VertxResource añada el siguiente método:
@GET (1)
@Path("/lorem")
public Uni<String> readShortFile() { (2)
return vertx.fileSystem().readFile("lorem.txt") (3)
.onItem().transform(content -> content.toString(StandardCharsets.UTF_8)); (4)
}
| 1 | Este endpoint gestiona la petición HTTP GET en la ruta /lorem (por lo que la ruta completa será vertx/lorem) |
| 2 | Como la API de Vert.x es asíncrona, nuestro método devuelve un Uni. El contenido se escribe en la respuesta HTTP cuando se completa la operación asíncrona representada por el Uni. |
| 3 | Utilizamos la API del sistema de archivos Vert.x para leer el archivo creado |
| 4 | Una vez leído el archivo, el contenido se almacena en un buffer en memoria. Transformamos este búfer en un String. |
En un terminal, navegue hasta la raíz del proyecto y ejecute:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
En otra terminal, ejecute:
> curl http://localhost:8080/vertx/lorem
Debería ver el contenido del archivo impreso en la consola.
| Quarkus proporciona otras formas de servir archivos estáticos. Este es un ejemplo hecho para la guía. |
Uso de la capacidad de flujo de Vert.x
Leer un archivo y almacenar su contenido en la memoria funciona para archivos pequeños, pero no para los grandes. En esta sección, veremos cómo puede utilizar la capacidad de transmisión de Vert.x.
En primer lugar, descargue Guerra y Paz y guárdelo en src/main/resources/book.txt.
Se trata de un archivo de 3,2 Mb que, aunque no es enorme, ilustra el propósito de los flujos.
Esta vez, no acumularemos el contenido del archivo en la memoria y lo escribiremos en un lote, sino que lo leeremos trozo a trozo y escribiremos estos trozos en la respuesta HTTP uno a uno.
Por lo tanto, debe tener los siguientes archivos en su proyecto:
.
├── mvnw
├── mvnw.cmd
├── pom.xml
├── README.md
├── src
│ └── main
│ ├── docker
│ │ ├── ...
│ ├── java
│ │ └── org
│ │ └── acme
│ │ └── VertxResource.java
│ └── resources
│ ├── application.properties
│ ├── book.txt
│ └── lorem.txt
Añada las siguientes importaciones al archivo src/main/java/org/acme/VertxResource.java:
import io.smallrye.mutiny.Multi;
import io.vertx.core.file.OpenOptions;
Añada el siguiente método a la clase VertxResource:
@GET
@Path("/book")
public Multi<String> readLargeFile() { (1)
return vertx.fileSystem().open("book.txt", (2)
new OpenOptions().setRead(true)
)
.onItem().transformToMulti(file -> file.toMulti()) (3)
.onItem().transform(content -> content.toString(StandardCharsets.UTF_8) (4)
+ "\n------------\n"); (5)
}
| 1 | Esta vez, devolvemos un Multi ya que queremos transmitir los trozos |
| 2 | Abrimos el archivo con el método open. Éste devuelve un Uni<AsyncFile> |
| 3 | Cuando se abre el archivo, recuperamos un Multi que contendrá los trozos. |
| 4 | Para cada trozo, producimos una cadena |
| 5 | Para ver los trozos en la respuesta, añadimos un separador |
Luego, en un terminal, ejecute:
> curl http://localhost:8080/vertx/book
Debería recuperar el contenido del libro. En la salida debería ver el separador como:
...
The little princess had also left the tea table and followed Hélène.
“Wait a moment, I’ll get my work.... Now then, what
------------
are you
thinking of?” she went on, turning to Prince Hippolyte. “Fetch me my
workbag.”
...
Uso del bus de eventos
Una de las características centrales de Vert.x es el bus de eventos.
Proporciona una columna vertebral basada en mensajes a su aplicación.
Así, puede tener componentes interactuando mediante el paso asíncrono de mensajes, y desacoplar así sus componentes.
Puede enviar un mensaje a un único consumidor, o despachar a múltiples consumidores, o implementar una interacción petición-respuesta, en la que envía un mensaje (petición) y espera una respuesta.
Esto es lo que vamos a utilizar en esta sección.
Nuestro VertxResource enviará un mensaje con un nombre a la dirección greetings.
Otro componente recibirá el mensaje y producirá la respuesta "hello $name".
El VertxResource recibirá la respuesta y la devolverá como respuesta HTTP.
So, first, add the following imports to the src/main/java/org/acme/VertxResource.java file:
import io.vertx.mutiny.core.eventbus.EventBus;
import jakarta.ws.rs.QueryParam;
A continuación, ampliemos nuestra clase VertxResource con el siguiente código:
@Inject
EventBus bus; (1)
@GET
@Path("/hello")
public Uni<String> hello(@QueryParam("name") String name) { (2)
return bus.<String>request("greetings", name) (3)
.onItem().transform(response -> response.body()); (4)
}
| 1 | Inyecte el bus de eventos. Alternativamente puede utilizar vertx.eventBus(). |
| 2 | Recibimos un name como parámetro de consulta |
| 3 | Utilizamos el método request para iniciar la interacción petición-respuesta. Enviamos el nombre a la dirección "greetings". |
| 4 | Cuando se recibe la respuesta, extraemos el cuerpo y lo devolvemos como respuesta HTTP |
Ahora necesitamos la otra parte: el componente que recibe el nombre y responde.
Cree el archivo src/main/java/org/acme/GreetingService.java con el siguiente contenido:
package org.acme;
import io.quarkus.vertx.ConsumeEvent;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped (1)
public class GreetingService {
@ConsumeEvent("greetings") (2)
public String hello(String name) { (3)
return "Hello " + name; (4)
}
}
| 1 | Declarar un Bean CDI en el ámbito de la Aplicación. Quarkus creará una única instancia de esta clase. |
| 2 | Utilice la anotación @ConsumeEvent para declarar un consumidor. También es posible utilizar directamente la API Vert.x. |
| 3 | Recibe la carga útil del mensaje como parámetro del método. El objeto devuelto será la respuesta. |
| 4 | Devuelve la respuesta. Esta respuesta se devuelve a la clase VertxResource |
Intentemos esto. En un terminal, ejecute:
> curl "http://localhost:8080/vertx/hello?name=bob"
Debería recibir de vuelta el mensaje Hello bob esperado.
Uso de clientes Vert.x
Hasta ahora, hemos utilizado la API básica de Vert.x. Vert.x ofrece mucho más. Proporciona un vasto ecosistema. En esta sección, veremos cómo puede utilizar el Cliente Web Vert.x, un cliente HTTP reactivo.
Tenga en cuenta que algunas extensiones de Quarkus envuelven a los clientes Vert.x y los gestionan por usted. Es el caso de las fuentes de datos reactivas, Redis, correo… No es el caso del Cliente Web.
Recuerde que, al principio de la guía, añadimos la dependencia smallrye-mutiny-vertx-web-client a nuestro archivo pom.xml.
Ahora es el momento de utilizarla.
First, add the following imports to the src/main/java/org/acme/VertxResource.java file:
import io.vertx.core.json.JsonArray;
import io.vertx.mutiny.ext.web.client.HttpResponse;
import io.vertx.mutiny.ext.web.client.WebClient;
A continuación, necesitamos crear una instancia de WebClient.
Amplíe la clase VertxResource con el campo client y la creación del cliente web en el constructor:
private final Vertx vertx;
private final WebClient client; (1)
@Inject
public VertxResource(Vertx vertx) {
this.vertx = vertx;
this.client = WebClient.create(vertx); (2)
}
| 1 | Almacene el WebClient, así podremos utilizarlo en nuestro endpoint HTTP |
| 2 | Cree el WebClient. Asegúrese de utilizar la clase io.vertx.mutiny.ext.web.client.WebClient |
Implementemos ahora un nuevo endpoint HTTP que consulte la API de Wikipedia para recuperar las páginas sobre Quarkus en los distintos idiomas.
Añada el siguiente método a la clase VertxResource:
private static final String URL = "https://en.wikipedia.org/w/api.php?action=parse&page=Quarkus&format=json&prop=langlinks";
@GET
@Path("/web")
public Uni<JsonArray> retrieveDataFromWikipedia() { (1)
return client.getAbs(URL).send() (2)
.onItem().transform(HttpResponse::bodyAsJsonObject) (3)
.onItem().transform(json -> json.getJsonObject("parse") (4)
.getJsonArray("langlinks"));
}
| 1 | Este endpoint devuelve un Array JSON. Vert.x proporciona una forma cómoda de manipular objetos y matrices JSON. Encontrará más detalles al respecto en la guía de referencia. |
| 2 | Envíe una petición GET a la API de Wikipedia |
| 3 | Una vez recibida la respuesta, extráigala como un objeto JSON |
| 4 | Extraiga el arreglo langlinks de la respuesta. |
A continuación, invoque el endpoint utilizando:
> curl http://localhost:8080/vertx/web
[{"lang":"de","url":"https://de.wikipedia.org/wiki/Quarkus","langname":"German","autonym":"Deutsch","*":"Quarkus"},{"lang":"fr","url":"https://fr.wikipedia.org/wiki/Quarkus","langname":"French","autonym":"français","*":"Quarkus"}]
La respuesta indica que, además de la página en inglés, hay una página en alemán y otra en francés sobre Quarkus en la wikipedia.
Ejecución de código asíncrono desde un hilo bloqueante
A veces es necesario ejecutar un código asíncrono desde un hilo bloqueante.
Concretamente, ejecutar el código en un hilo Vert.x con un contexto Vert.x aislado/duplicado.
Un ejemplo típico es un código asíncrono que necesita aprovechar la API Reactiva de Hibernate durante el inicio de la aplicación.
Quarkus proporciona el método VertxContextSupport#subscribeAndAwait() que se suscribe al io.smallrye.mutiny.Uni suministrado en un contexto Vert.x duplicado, luego bloquea el hilo actual y espera el resultado.
void onStart(@Observes StartupEvent event, Mutiny.SessionFactory sf) {
VertxContextSupport.subscribeAndAwait(() -> {
return sf.withTransaction(session -> session.persist(new Person()));
});
}
| Si es necesario, el contexto de petición CDI se activa durante la ejecución del código asíncrono. |
¡VertxContextSupport#subscribeAndAwait() no debe llamarse en un bucle de eventos!
|
También es posible suscribirse a un io.smallrye.mutiny.Multi suministrado en un contexto duplicado Vert.x.
En este caso, el hilo actual no se bloquea y se utiliza la lógica de suscripción suministrada para consumir los eventos.
void onStart(@Observes StartupEvent event, ExternalService service) {
VertxContextSupport.subscribeWith(() -> service.getFoos(), foo -> {
// do something useful with foo
});
}
Ir más allá
Esta guía presenta cómo puede utilizar las API de Vert.x desde una aplicación Quarkus. Es solo un breve resumen. Si desea saber más, consulte la guía de referencia sobre Vert.x en Quarkus.
Como hemos visto, el bus de eventos es el tejido conectivo de las aplicaciones Vert.x. Quarkus lo integra para que distintos beans puedan interactuar con mensajes asíncronos. Esta parte está cubierta en la documentación del bus de eventos.
Learn how to implement highly performant, low-overhead database applications on Quarkus with the Reactive SQL Clients.