Guía de referencia Vert.x
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.
Esta guía es el complemento de la guía Uso de la API Vert.x de Eclipse desde una aplicación Quarkus. Proporciona detalles más avanzados sobre el uso y la configuración de la instancia Vert.x utilizada por Quarkus.
Accessing the Vert.x instance
To access the managed Vert.x instance, add the quarkus-vertx extension to your project.
Note that this dependency may already be installed (as a transitive dependency).
Con esta extensión, puede recuperar la instancia gestionada de Vert.x utilizando la inyección por campo o constructor:
@ApplicationScoped
public class MyBean {
// Field injection
@Inject Vertx vertx;
// Constructor injection
MyBean(Vertx vertx) {
// ...
}
}
Puede inyectar:
-
la instancia
io.vertx.core.Vertxque expone la API Vert.x bare -
la instancia
io.vertx.mutiny.core.Vertxque expone la API Mutiny
Recomendamos utilizar la variante Mutiny, ya que se integra con las demás APIs reactivas proporcionadas por Quarkus.
|
Mutiny
Si no está familiarizado con Mutiny, consulte Mutiny - una biblioteca de programación reactiva intuitiva. |
La documentación sobre la variante Vert.x Mutiny está disponible en smallrye.io.
Configuring the Vert.x instance
Puede configurar la instancia Vert.x desde el archivo application.properties.
La siguiente tabla enumera las propiedades admitidas:
Propiedad de configuración fijada en tiempo de compilación - Todas las demás propiedades de configuración son anulables en tiempo de ejecución
Tipo |
Por defecto |
|
|---|---|---|
Enables or disables the Vert.x cache. Environment variable: Show more |
boolean |
|
Enables or disabled the Vert.x classpath resource resolver. Environment variable: Show more |
boolean |
|
The number of event loops. By default, it matches the number of CPUs detected on the system. Environment variable: Show more |
int |
|
The maximum amount of time the event loop can be blocked. Environment variable: Show more |
|
|
The amount of time before a warning is displayed if the event loop is blocked. Environment variable: Show more |
|
|
The size of the worker thread pool. Environment variable: Show more |
int |
|
The maximum amount of time the worker thread can be blocked. Environment variable: Show more |
|
|
The size of the internal thread pool (used for the file system). Environment variable: Show more |
int |
|
The queue size. For most applications this should be unbounded Environment variable: Show more |
int |
|
The executor growth resistance. A resistance factor applied after the core pool is full; values applied here will cause that fraction of submissions to create new threads when no idle thread is available. A value of Environment variable: Show more |
float |
|
The amount of time a thread will stay alive with no work. Environment variable: Show more |
|
|
Prefill thread pool when creating a new Executor. When io.vertx.core.spi.ExecutorServiceFactory.createExecutor is called, initialise with the number of defined threads at startup Environment variable: Show more |
boolean |
|
Enables the async DNS resolver. Environment variable: Show more |
boolean |
|
PEM Key/cert config is disabled by default. Environment variable: Show more |
boolean |
|
Comma-separated list of the path to the key files (Pem format). Environment variable: Show more |
list of string |
|
Comma-separated list of the path to the certificate files (Pem format). Environment variable: Show more |
list of string |
|
JKS config is disabled by default. Environment variable: Show more |
boolean |
|
Path of the key file (JKS format). Environment variable: Show more |
string |
|
Password of the key file. Environment variable: Show more |
string |
|
PFX config is disabled by default. Environment variable: Show more |
boolean |
|
Path to the key file (PFX format). Environment variable: Show more |
string |
|
Password of the key. Environment variable: Show more |
string |
|
PEM Trust config is disabled by default. Environment variable: Show more |
boolean |
|
Comma-separated list of the trust certificate files (Pem format). Environment variable: Show more |
list of string |
|
JKS config is disabled by default. Environment variable: Show more |
boolean |
|
Path of the key file (JKS format). Environment variable: Show more |
string |
|
Password of the key file. Environment variable: Show more |
string |
|
PFX config is disabled by default. Environment variable: Show more |
boolean |
|
Path to the key file (PFX format). Environment variable: Show more |
string |
|
Password of the key. Environment variable: Show more |
string |
|
The accept backlog. Environment variable: Show more |
int |
|
The client authentication. Environment variable: Show more |
string |
|
The connect timeout. Environment variable: Show more |
|
|
The idle timeout in milliseconds. Environment variable: Show more |
||
The receive buffer size. Environment variable: Show more |
int |
|
The number of reconnection attempts. Environment variable: Show more |
int |
|
The reconnection interval in milliseconds. Environment variable: Show more |
|
|
Whether to reuse the address. Environment variable: Show more |
boolean |
|
Whether to reuse the port. Environment variable: Show more |
boolean |
|
The send buffer size. Environment variable: Show more |
int |
|
The so linger. Environment variable: Show more |
int |
|
Enables or Disabled SSL. Environment variable: Show more |
boolean |
|
Whether to keep the TCP connection opened (keep-alive). Environment variable: Show more |
boolean |
|
Configure the TCP no delay. Environment variable: Show more |
boolean |
|
Configure the traffic class. Environment variable: Show more |
int |
|
Enables or disables the trust all parameter. Environment variable: Show more |
boolean |
|
The host name. Environment variable: Show more |
string |
|
int |
||
The public host name. Environment variable: Show more |
string |
|
The public port. Environment variable: Show more |
int |
|
Enables or disables the clustering. Environment variable: Show more |
boolean |
|
The ping interval. Environment variable: Show more |
|
|
The ping reply interval. Environment variable: Show more |
|
|
The maximum amount of time in seconds that a successfully resolved address will be cached. If not set explicitly, resolved addresses may be cached forever. Environment variable: Show more |
int |
|
The minimum amount of time in seconds that a successfully resolved address will be cached. Environment variable: Show more |
int |
|
The amount of time in seconds that an unsuccessful attempt to resolve an address will be cached. Environment variable: Show more |
int |
|
The maximum number of queries to be sent during a resolution. Environment variable: Show more |
int |
|
The duration after which a DNS query is considered to be failed. Environment variable: Show more |
|
|
Enable or disable native transport Environment variable: Show more |
boolean |
|
|
About the Duration format
El formato de las duraciones utiliza el formato estándar También puede proporcionar valores de duración que empiecen por un número. En este caso, si el valor consiste sólo en un número, el conversor trata el valor como segundos. En caso contrario, |
Using Vert.x clients
Además del núcleo Vert.x, puede utilizar la mayoría de las bibliotecas del ecosistema Vert.x. Algunas extensiones de Quarkus ya envuelven bibliotecas Vert.x.
APIs disponibles
The following table lists the most used libraries from the Vert.x ecosystem. To access these APIs, add the indicated extension or dependency to your project. Refer to the associated documentation to learn how to use them.
API |
Extensión o dependencia |
Documentación |
Cliente AMQP |
|
|
Interruptor automático |
|
|
Cliente Consul |
|
|
Cliente DB2 |
|
|
Cliente Kafka |
|
|
Cliente de correo |
|
|
Cliente MQTT |
|
Todavía no hay guía |
Cliente MS SQL |
|
|
Cliente MySQL |
|
|
Cliente Oracle |
|
|
Cliente PostgreSQL |
|
|
Cliente RabbitMQ |
|
|
Cliente Redis |
|
|
Cliente web |
|
Para saber más sobre el uso de la API de Vert.x Mutiny, consulte smallrye.io.
Example of usage
This section gives an example using the Vert.x WebClient in the context of a RESTEasy Reactive application.
As indicated in the table above, add the following dependency to your project:
<dependency>
<groupId>io.smallrye.reactive</groupId>
<artifactId>smallrye-mutiny-vertx-web-client</artifactId>
</dependency>
implementation("io.smallrye.reactive:smallrye-mutiny-vertx-web-client")
Ahora, en su código, puede crear una instancia de WebClient:
package org.acme.vertx;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.Vertx;
import io.vertx.mutiny.ext.web.client.WebClient;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClientOptions;
@Path("/fruit-data")
public class ResourceUsingWebClient {
private final WebClient client;
@Inject
VertxResource(Vertx vertx) {
this.client = WebClient.create(vertx);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{name}")
public Uni<JsonObject> getFruitData(String name) {
return client.getAbs("https://.../api/fruit/" + name)
.send()
.onItem().transform(resp -> {
if (resp.statusCode() == 200) {
return resp.bodyAsJsonObject();
} else {
return new JsonObject()
.put("code", resp.statusCode())
.put("message", resp.bodyAsString());
}
});
}
}
Este recurso crea un WebClient y, al recibir una petición, utiliza este cliente para invocar una API HTTP remota.
Dependiendo del resultado, la respuesta se reenvía tal y como se recibió, o crea un objeto JSON que envuelve el error.
El WebClient es asíncrono (y no bloqueante), por lo que el endpoint devuelve un Uni.
La aplicación también puede ejecutarse como un ejecutable nativo.
Pero, primero, necesitamos instruir a Quarkus para que habilite ssl (si la API remota utiliza HTTPS).
Abra el src/main/resources/application.properties y añada:
quarkus.ssl.native=true
Luego, cree el ejecutable nativo con:
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.package.type=native
Using Vert.x JSON
Las APIs de Vert.x dependen a menudo de JSON.
Vert.x proporciona dos clases convenientes para manipular documentos JSON: io.vertx.core.json.JsonObject y io.vertx.core.json.JsonArray.
JsonObject se puede utilizar para mapear un objeto en su representación JSON y construir un objeto a partir de un documento JSON:
// Map an object into JSON
Person person = ...;
JsonObject json = JsonObject.mapFrom(person);
// Build an object from JSON
json = new JsonObject();
person = json.mapTo(Person.class);
Tenga en cuenta que estas funciones utilizan el mapeador gestionado por la extensión quarkus-jackson.
Consulte configuración de Jackson para personalizar el mapeo.
JSON Object and JSON Array are both supported as Quarkus HTTP endpoint requests and response bodies (using classic RESTEasy and RESTEasy Reactive). Consider these endpoints:
package org.acme.vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.core.json.JsonArray;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/hello")
@Produces(MediaType.APPLICATION_JSON)
public class VertxJsonResource {
@GET
@Path("{name}/object")
public JsonObject jsonObject(String name) {
return new JsonObject().put("Hello", name);
}
@GET
@Path("{name}/array")
public JsonArray jsonArray(String name) {
return new JsonArray().add("Hello").add(name);
}
}
{"Hello":"Quarkus"}
["Hello","Quarkus"]
Esto funciona igualmente bien cuando el contenido JSON es un cuerpo de petición o está envuelto en un Uni, Multi, CompletionStage o Publisher.
Using verticles
Verticles es "un modelo de despliegue y concurrencia simple, escalable y similar a un actor" proporcionado por _Vert.x. Este modelo no pretende ser una implementación estricta del modelo actor, pero comparte similitudes, especialmente en lo que respecta a la concurrencia, el escalado y el despliegue. Para utilizar este modelo, usted escribe y despliega vértices, que se comunican mediante el envío de mensajes en el bus de eventos.
Puede desplegar vértices en Quarkus. Es compatible con:
-
vértice bare - clases Java que extienden
io.vertx.core.AbstractVerticle -
vértice Mutiny - clases Java que extienden
io.smallrye.mutiny.vertx.core.AbstractVerticle
Deploying verticles
Para desplegar vértices, utilice el método deployVerticle:
@Inject Vertx vertx;
// ...
vertx.deployVerticle(MyVerticle.class.getName(), ar -> { });
vertx.deployVerticle(new MyVerticle(), ar -> { });
Si utiliza la variante Mutiny de Vert.x, tenga en cuenta que el método deployVerticle devuelve un Uni, y tendría que activar una suscripción para realizar el despliegue real.
| A continuación, un ejemplo que explica cómo desplegar vértices durante la inicialización de la aplicación. |
Using @ApplicationScoped Beans as Verticle
En general, los vértices Vert.x no son beans CDI. Por lo tanto, no pueden utilizar la inyección. Sin embargo, en Quarkus, puede desplegar vértices como beans. Tenga en cuenta que, en este caso, CDI (Arc en Quarkus) se encarga de crear la instancia.
El siguiente fragmento ofrece un ejemplo:
package io.quarkus.vertx.verticles;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.vertx.core.AbstractVerticle;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class MyBeanVerticle extends AbstractVerticle {
@ConfigProperty(name = "address") String address;
@Override
public Uni<Void> asyncStart() {
return vertx.eventBus().consumer(address)
.handler(m -> m.replyAndForget("hello"))
.completionHandler();
}
}
No tiene que inyectar la instancia vertx; en su lugar, aproveche el campo protegido de AbstractVerticle.
Luego, despliegue las instancias de vértice con:
package io.quarkus.vertx.verticles;
import io.quarkus.runtime.StartupEvent;
import io.vertx.mutiny.core.Vertx;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
@ApplicationScoped
public class VerticleDeployer {
public void init(@Observes StartupEvent e, Vertx vertx, MyBeanVerticle verticle) {
vertx.deployVerticle(verticle).await().indefinitely();
}
}
Si quiere desplegar cada AbstractVerticle expuesto, puede utilizar:
public void init(@Observes StartupEvent e, Vertx vertx, Instance<AbstractVerticle> verticles) {
for (AbstractVerticle verticle : verticles) {
vertx.deployVerticle(verticle).await().indefinitely();
}
}
Using multiple verticles instances
Cuando utilice @ApplicationScoped, obtendrá una única instancia para su vértice.
Tener varias instancias de vértices puede ser útil para repartir la carga entre ellas.
Cada una de ellas estará asociada a un hilo de E/S diferente (bucle de eventos Vert.x).
Para desplegar varias instancias de su vértice, utilice el ámbito @Dependent en lugar de @ApplicationScoped:
package org.acme.verticle;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.vertx.core.AbstractVerticle;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
@Dependent
public class MyVerticle extends AbstractVerticle {
@Override
public Uni<Void> asyncStart() {
return vertx.eventBus().consumer("address")
.handler(m -> m.reply("Hello from " + this))
.completionHandler();
}
}
A continuación, despliegue su vértice de la siguiente manera:
package org.acme.verticle;
import io.quarkus.runtime.StartupEvent;
import io.vertx.core.DeploymentOptions;
import io.vertx.mutiny.core.Vertx;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
@ApplicationScoped
public class MyApp {
void init(@Observes StartupEvent ev, Vertx vertx, Instance<MyVerticle> verticles) {
vertx
.deployVerticle(verticles::get, new DeploymentOptions().setInstances(2))
.await().indefinitely();
}
}
El método init recibe un Instance<MyVerticle>.
A continuación, pasa un proveedor al método deployVerticle.
El proveedor no hace más que llamar al método get().
Gracias al ámbito @Dependent, devuelve una nueva instancia en cada llamada.
Por último, pasa el número deseado de instancias a DeploymentOptions, como dos en el ejemplo anterior.
Llamará al proveedor dos veces, lo que creará dos instancias de su vértice.
Uso del bus de eventos
Vert.x viene con un bus de eventos incorporado que puede utilizar desde su aplicación Quarkus. De este modo, los componentes de su aplicación (beans CDI, recursos…) pueden interactuar utilizando eventos asíncronos, promoviendo así el acoplamiento débil.
Con el bus de eventos, usted envía mensajes a direcciones virtuales. El bus de eventos ofrece tres tipos de mecanismos de entrega:
-
punto a punto - se envía el mensaje, un consumidor lo recibe. Si varios consumidores escuchan la dirección, se aplica un round-robin;
-
publicar/suscribir - publicar un mensaje; todos los consumidores que escuchan la dirección reciben el mensaje;
-
petición/respuesta - se envía el mensaje y se espera una respuesta. El receptor puede responder al mensaje de forma asíncrona.
Todos estos mecanismos de entrega son no bloqueantes y proporcionan uno de los elementos fundamentales para construir aplicaciones reactivas.
Consuming events
Aunque puede utilizar la API de Vert.x para registrar consumidores, Quarkus viene con soporte declarativo.
Para consumir eventos, utilice la anotación io.quarkus.vertx.ConsumeEvent:
package org.acme.vertx;
import io.quarkus.vertx.ConsumeEvent;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class GreetingService {
@ConsumeEvent (1)
public String consume(String name) { (2)
return name.toUpperCase();
}
}
| 1 | Si no se establece, la dirección es el nombre completo del bean; por ejemplo, en este fragmento, es org.acme.vertx.GreetingService. |
| 2 | El parámetro del método es el cuerpo del mensaje. Si el método devuelve algo, es la respuesta del mensaje. |
Configuring the address
La anotación @ConsumeEvent se puede configurar para establecer la dirección:
@ConsumeEvent("greeting") (1)
public String consume(String name) {
return name.toUpperCase();
}
| 1 | Recibir los mensajes enviados a la dirección greeting |
Asynchronous processing
Los ejemplos anteriores utilizan el procesamiento síncrono.
El procesamiento asíncrono también es posible devolviendo un io.smallrye.mutiny.Uni o un java.util.concurrent.CompletionStage:
package org.acme.vertx;
import io.quarkus.vertx.ConsumeEvent;
import javax.enterprise.context.ApplicationScoped;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import io.smallrye.mutiny.Uni;
@ApplicationScoped
public class GreetingService {
@ConsumeEvent
public CompletionStage<String> consume(String name) {
// return a CompletionStage completed when the processing is finished.
// You can also fail the CompletionStage explicitly
}
@ConsumeEvent
public Uni<String> process(String name) {
// return an Uni completed when the processing is finished.
// You can also fail the Uni explicitly
}
}
|
Mutiny
El ejemplo anterior utiliza tipos reactivos Mutiny. Si no está familiarizado con Mutiny, consulte Mutiny - una biblioteca de programación reactiva intuitiva. |
Blocking processing
Por defecto, el código que consume el evento debe ser no bloqueante, ya que se llama en un hilo de E/S.
Si su procesamiento es bloqueante, utilice la anotación @io.smallrye.common.annotation.Blocking:
@ConsumeEvent(value = "blocking-consumer")
@Blocking
void consumeBlocking(String message) {
// Something blocking
}
Alternativamente, puede utilizar el atributo blocking de la anotación @ConsumeEvent:
@ConsumeEvent(value = "blocking-consumer", blocking = true)
void consumeBlocking(String message) {
// Something blocking
}
Cuando se utiliza @Blocking, se ignora el valor del atributo blocking de @ConsumeEvent.
Replying to messages
El valor de retorno de un método anotado con @ConsumeEvent se utiliza para responder al mensaje entrante.
Por ejemplo, en el siguiente fragmento, el String devuelto es la respuesta.
@ConsumeEvent("greeting")
public String consume(String name) {
return name.toUpperCase();
}
También puede devolver un Uni<T> o un CompletionStage<T> para manejar la respuesta asíncrona:
@ConsumeEvent("greeting")
public Uni<String> consume2(String name) {
return Uni.createFrom().item(() -> name.toUpperCase()).emitOn(executor);
}
|
Puede inyectar un
|
Implementing fire and forget interactions
No es necesario responder a los mensajes recibidos.
Normalmente, para una interacción de disparar y olvidar, los mensajes se consumen y el remitente no necesita saberlo.
Para implementar este patrón, su método consumidor devuelve void.
@ConsumeEvent("greeting")
public void consume(String event) {
// Do something with the event
}
Dealing with messages
A diferencia del ejemplo anterior en el que se utilizaban directamente las cargas útiles, también se puede utilizar directamente Message:
@ConsumeEvent("greeting")
public void consume(Message<String> msg) {
System.out.println(msg.address());
System.out.println(msg.body());
}
Handling Failures
Si un método anotado con @ConsumeEvent lanza una excepción, entonces:
-
si se establece un manejador de respuesta, entonces el fallo se propaga de vuelta al remitente a través de un
io.vertx.core.eventbus.ReplyExceptioncon el códigoConsumeEvent#FAILURE_CODEy el mensaje de excepción, -
if no reply handler is set, then the exception is rethrown (and wrapped in a
RuntimeExceptionif necessary) and can be handled by the default exception handler, i.e.io.vertx.core.Vertx#exceptionHandler().
Sending messages
El envío y la publicación de mensajes utilizan el bus de eventos Vert.x:
package org.acme.vertx;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.eventbus.EventBus;
import io.vertx.mutiny.core.eventbus.Message;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/async")
public class EventResource {
@Inject
EventBus bus; (1)
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("{name}")
public Uni<String> greeting(String name) {
return bus.<String>request("greeting", name) (2)
.onItem().transform(Message::body);
}
}
| 1 | Inyectar el bus de eventos |
| 2 | Envía un mensaje a la dirección greeting. El contenido del mensaje es name |
El objeto EventBus proporciona métodos para:
-
sendun mensaje a una dirección específica: un solo consumidor recibe el mensaje. -
publishun mensaje a una dirección específica: todos los consumidores reciben los mensajes. -
requestun mensaje y esperar una respuesta
// Case 1
bus.sendAndForget("greeting", name)
// Case 2
bus.publish("greeting", name)
// Case 3
Uni<String> response = bus.<String>request("address", "hello, how are you?")
.onItem().transform(Message::body);
Using codecs
The Vert.x Event Bus uses codecs to serialize and deserialize objects. Quarkus provides a default codec for local delivery. So you can exchange objects as follows:
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("{name}")
public Uni<String> greeting(String name) {
return bus.<String>request("greeting", new MyName(name))
.onItem().transform(Message::body);
}
@ConsumeEvent(value = "greeting")
Uni<String> greeting(MyName name) {
return Uni.createFrom().item(() -> "Hello " + name.getName());
}
Si quiere usar un códec específico, tiene que configurarlo en ambos extremos explícitamente:
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("{name}")
public Uni<String> greeting(String name) {
return bus.<String>request("greeting", name,
new DeliveryOptions().setCodecName(MyNameCodec.class.getName())) (1)
.onItem().transform(Message::body);
}
@ConsumeEvent(value = "greeting", codec = MyNameCodec.class) (2)
Uni<String> greeting(MyName name) {
return Uni.createFrom().item(() -> "Hello "+name.getName());
}
| 1 | Establezca el nombre del códec que se utilizará para enviar el mensaje |
| 2 | Establezca el códec que se utilizará para recibir el mensaje |
Combining HTTP and the event bus
Let’s revisit a greeting HTTP endpoint and use asynchronous message passing to delegate the call to a separated bean. It uses the request/reply dispatching mechanism. Instead of implementing the business logic inside the JAX-RS endpoint, we are sending a message. Another bean consumes this message, and the response is sent using the reply mechanism.
En su clase de endpoint HTTP, inyecte el bus de eventos y utilice el método request para enviar un mensaje al bus de eventos y esperar una respuesta:
package org.acme.vertx;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.eventbus.EventBus;
import io.vertx.mutiny.core.eventbus.Message;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/bus")
public class EventResource {
@Inject
EventBus bus;
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("{name}")
public Uni<String> greeting(String name) {
return bus.<String>request("greeting", name) (1)
.onItem().transform(Message::body); (2)
}
}
| 1 | enviar el name a la dirección greeting y solicitar una respuesta |
| 2 | cuando obtenemos la respuesta, extraer el cuerpo y enviarlo al usuario |
the HTTP method returns a Uni.
If you are using RESTEasy Reactive, Uni support is built-in.
If you are using classic RESTEasy, you need to add the quarkus resteasy-mutiny extension to your project.
|
Necesitamos un consumidor que escuche en la dirección greeting.
Este consumidor puede estar en la misma clase o en otro bean como:
package org.acme.vertx;
import io.quarkus.vertx.ConsumeEvent;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class GreetingService {
@ConsumeEvent("greeting")
public String greeting(String name) {
return "Hello " + name;
}
}
Este bean recibe el nombre y devuelve el mensaje de saludo.
Con esto en marcha, cada petición HTTP en /bus/quarkus envía un mensaje al bus de eventos, espera una respuesta, y cuando esta llega, escribe la respuesta HTTP:
Hello Quarkus
Para entenderlo mejor, vamos a detallar cómo se ha gestionado la petición/respuesta HTTP:
-
La petición es recibida por el método
greeting -
se envía un mensaje con el name al bus de eventos
-
Otro bean recibe este mensaje y calcula la respuesta
-
Esta respuesta se devuelve mediante el mecanismo de respuesta
-
Una vez que el remitente recibe la respuesta, el contenido se escribe en la respuesta HTTP
Bidirectional communication with browsers using SockJS
El puente SockJS proporcionado por Vert.x permite que las aplicaciones del navegador y las aplicaciones de Quarkus se comuniquen utilizando el bus de eventos. Conecta ambos lados. Así, ambos lados pueden enviar mensajes recibidos en el otro lado. Soporta los tres mecanismos de entrega.
SockJS negocia el canal de comunicación entre la aplicación Quarkus y el navegador. Si se admiten WebSockets, los utiliza; de lo contrario, se degrada a SSE, sondeo largo, etc.
Para utilizar SockJS, es necesario configurar el puente, especialmente las direcciones que se utilizarán para comunicarse:
package org.acme.vertx;
import io.vertx.core.Vertx;
import io.vertx.ext.bridge.PermittedOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.sockjs.SockJSBridgeOptions;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import java.util.concurrent.atomic.AtomicInteger;
@ApplicationScoped
public class SockJsExample {
@Inject
Vertx vertx;
public void init(@Observes Router router) {
SockJSHandler sockJSHandler = SockJSHandler.create(vertx);
sockJSHandler.bridge(new SockJSBridgeOptions()
.addOutboundPermitted(new PermittedOptions().setAddress("ticks")));
router.route("/eventbus/*").handler(sockJSHandler);
}
}
Este código configura el puente SockJS para que envíe todos los mensajes dirigidos a la dirección ticks a los navegadores conectados.
Encontrará explicaciones más detalladas sobre la configuración en la documentación del puente SockJS de Vert.x.
El navegador debe utilizar la biblioteca JavaScript vertx-eventbus para consumir el mensaje:
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>SockJS example - Quarkus</title>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script type="application/javascript" src="https://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vertx3-eventbus-client@3.8.5/vertx-eventbus.min.js"></script>
</head>
<body>
<h1>SockJS Examples</h1>
<p><strong>Last Tick:</strong> <span id="tick"></span></p>
</body>
<script>
var eb = new EventBus('/eventbus');
eb.onopen = function () {
eb.registerHandler('ticks', function (error, message) {
$("#tick").html(message.body);
});
}
</script>
</html>
Native Transport
| Native transports are not supported in GraalVM produced binaries. |
Vert.x is capable of using Netty’s native transports, which offers performance improvements on specific platforms.To enable them, you must include the appropriate dependency for your platform. It’s usually a good idea to have both to keep your application platform-agnostic. Netty is smart enough to use the correct one, that includes none at all on unsupported platforms:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-kqueue</artifactId>
<classifier>osx-x86_64</classifier>
</dependency>
implementation("io.netty:netty-transport-native-epoll::linux-x86_64")
implementation("io.netty:netty-transport-native-kqueue::osx-x86_64")
También tendrá que configurar explícitamente Vert.x para que utilice el transporte nativo.
En application.properties añada:
quarkus.vertx.prefer-native-transport=true
O en application.yml:
quarkus:
vertx:
prefer-native-transport: true
Si todo va bien, Quarkus registrará:
[io.qua.ver.cor.run.VertxCoreRecorder] (main) Vertx tiene habilitado el transporte nativo: true
Listening to a Unix Domain Socket
Escuchar en un socket de dominio Unix nos permite prescindir de la sobrecarga de TCP si la conexión al servicio Quarkus se establece desde el mismo host. Esto puede ocurrir si el acceso al servicio se realiza a través de un proxy, lo que suele ser el caso si está configurando una malla de servicios con un proxy como Envoy.
| Esto solo funcionará en plataformas que admitan Native Transport. |
Habilite el Native Transport correspondiente y establezca la siguiente propiedad de entorno:
quarkus.http.domain-socket=/var/run/io.quarkus.app.socket quarkus.http.domain-socket-enabled=true quarkus.vertx.prefer-native-transport=true
Por sí mismo esto no desactivará el socket TCP que por defecto se abrirá en
0.0.0.0:8080. Puede desactivarse explícitamente:
quarkus.http.host-enabled=false
Estas propiedades pueden establecerse a través del parámetro de línea de comandos -D de Java o
en application.properties.
| No olvide añadir la dependencia de transporte nativo. Consulte Native Transport para más detalles. |
| Asegúrese de que su aplicación tiene los permisos adecuados para escribir en el socket. |
Read only deployment environments
En entornos con sistemas de archivos de solo lectura puede recibir errores de la forma:
java.lang.IllegalStateException: Failed to create cache dir
Assuming /tmp/ is writable this can be fixed by setting the vertx.cacheDirBase property to point to a directory in /tmp/ for instance in OpenShift by creating an environment variable JAVA_OPTS with the value -Dvertx.cacheDirBase=/tmp/vertx.
Customizing the Vert.x configuration
La configuración de la instancia Vert.x gestionada puede proporcionarse mediante el archivo application.properties, pero también mediante beans especiales.
Los beans CDI que exponen la interfaz io.quarkus.vertx.VertxOptionsCustomizer pueden utilizarse para personalizar la configuración de Vert.x.
Por ejemplo, el siguiente personalizador cambia el directorio base tmp:
@ApplicationScoped
public class MyCustomizer implements VertxOptionsCustomizer {
@Override
public void accept(VertxOptions options) {
options.setFileSystemOptions(new FileSystemOptions().setFileCacheDir("target"));
}
}
Los beans customizer reciben los VertxOptions (provenientes de la configuración de la aplicación) y pueden modificarlos.