Getting Started to Quarkus Messaging with RabbitMQ
This guide demonstrates how your Quarkus application can utilize Quarkus Messaging to interact with RabbitMQ.
Essa tecnologia é considerada preview. In preview, backward compatibility and presence in the ecosystem is not guaranteed. Specific improvements might require changing configuration or APIs, and plans to become stable are under way. Feedback is welcome on our mailing list or as issues in our GitHub issue tracker. Para obter uma lista completa de possíveis status, consulte nosso FAQ. |
Pré-requisitos
Para concluir este guia, você precisa:
-
Cerca de 15 minutos
-
Um IDE
-
JDK 17+ installed with
JAVA_HOME
configured appropriately -
Apache Maven 3.9.9
-
Docker e Docker Compose ou Podman e Docker Compose
-
Opcionalmente, o Quarkus CLI se você quiser usá-lo
-
Opcionalmente, Mandrel ou GraalVM instalado e configurado apropriadamente se você quiser criar um executável nativo (ou Docker se você usar uma compilação de contêiner nativo)
Arquitetura
Neste guia, vamos desenvolver duas aplicações que se comunicam com o RabbitMQ. A primeira aplicação envia uma solicitação de cotação para a fila _ quote requests do RabbitMQ e consome mensagens da fila quote . A segunda aplicação recebe a mensagem da fila quote request e envia para a fila quote de volta.
A primeira aplicação, o producer
, permitirá que o usuário solicite algumas cotações por meio de um endpoint HTTP. Para cada solicitação de cotação, um identificador aleatório é gerado e retornado ao usuário, para colocar a solicitação de cotação em espera . Ao mesmo tempo, o ID da solicitação gerada é enviado para a fila quote-requests
.
A segunda aplicação, a processor
, por sua vez, lê a partir da fila quote-requests
coloca um preço aleatório na cotação e envia-o para uma fila chamada quotes
.
Por fim, o site producer
lerá as cotações e as enviará ao navegador usando eventos enviados pelo servidor. Portanto, o usuário verá o preço da cotação pendente para o preço recebido em tempo real.
Solução
Recomendamos que siga as instruções nas próximas seções e crie aplicativos passo a passo. No entanto, você pode ir direto para o exemplo concluído.
Clone o repositório Git: git clone https://github.com/quarkusio/quarkus-quickstarts.git
, ou faça o download em arquivo.
A solução rabbitmq-quickstart
está localizada no directory.
Criando o projeto Maven
Em primeiro lugar, temos de criar dois projetos: o producer e o processor.
Para criar o projeto producer, em um terminal, execute:
Para usuários do Windows:
-
Se estiver usando cmd, (não use barra invertida '\' e coloque tudo na mesma linha)
-
Se estiver usando o Powershell, envolva os parâmetros '-D' entre aspas duplas, por exemplo, '"-DprojectArtifactId=rabbitmq-quickstart-producer"'
Este comando cria a estrutura do projeto e seleciona as duas extensões Quarkus que vamos utilizar:
-
O conector RabbitMQ de mensagens reactivas
-
Quarkus REST (formerly RESTEasy Reactive) and its Jackson support to handle JSON payloads
If you already have your Quarkus project configured, you can add the CLI
Maven
Gradle
Isso adicionará o seguinte ao seu arquivo pom.xml
build.gradle
|
Para criar o projeto do processor, a partir do mesmo diretório, execute:
Para usuários do Windows:
-
Se estiver usando cmd, (não use barra invertida '\' e coloque tudo na mesma linha)
-
Se estiver usando o Powershell, envolva os parâmetros '-D' entre aspas duplas, por exemplo, '"-DprojectArtifactId=rabbitmq-quickstart-processor"'
Nesse momento, você deverá ter a seguinte estrutura:
.
├── rabbitmq-quickstart-processor
│ ├── README.md
│ ├── mvnw
│ ├── mvnw.cmd
│ ├── pom.xml
│ └── src
│ └── main
│ ├── docker
│ ├── java
│ └── resources
│ └── application.properties
└── rabbitmq-quickstart-producer
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── main
├── docker
├── java
└── resources
└── application.properties
Abra os dois projetos no seu IDE preferido.
O objeto Quote
A classe Quote
será usada nos projetos producer
e processor
. Para simplificar, vamos duplicar a classe. Em ambos os projetos, crie o arquivo src/main/java/org/acme/rabbitmq/model/Quote.java
, com o seguinte conteúdo:
package org.acme.rabbitmq.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
@RegisterForReflection
public class Quote {
public String id;
public int price;
/**
* Default constructor required for Jackson serializer
*/
public Quote() { }
public Quote(String id, int price) {
this.id = id;
this.price = price;
}
@Override
public String toString() {
return "Quote{" +
"id='" + id + '\'' +
", price=" + price +
'}';
}
}
A representação JSON dos objetos Quote
será usada nas mensagens enviadas para as filas do RabbitMQ e também nos eventos enviados pelo servidor para os clientes do navegador.
O Quarkus tem capacidades incorporadas para lidar com mensagens JSON do RabbitMQ.
@RegisterForReflection
The |
Enviando pedido de cotação
Dentro do projeto producer
, localize o arquivo src/main/java/org/acme/rabbitmq/producer/QuotesResource.java
gerado e atualize o conteúdo para que fique assim:
package org.acme.rabbitmq.producer;
import java.util.UUID;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.acme.rabbitmq.model.Quote;
import org.eclipse.microprofile.reactive.messaging.Channel;
import org.eclipse.microprofile.reactive.messaging.Emitter;
import io.smallrye.mutiny.Multi;
@Path("/quotes")
public class QuotesResource {
@Channel("quote-requests") Emitter<String> quoteRequestEmitter; (1)
/**
* Endpoint to generate a new quote request id and send it to "quote-requests" channel (which
* maps to the "quote-requests" RabbitMQ exchange) using the emitter.
*/
@POST
@Path("/request")
@Produces(MediaType.TEXT_PLAIN)
public String createRequest() {
UUID uuid = UUID.randomUUID();
quoteRequestEmitter.send(uuid.toString()); (2)
return uuid.toString();
}
}
1 | Injete um serviço de mensagens reativas Emitter para enviar mensagens para o canal quote-requests . |
2 | Em uma solicitação de postagem, gere um UUID aleatório e envie-o para a fila do RabbitMQ usando o emissor. |
Esse canal é mapeado para uma troca RabbitMQ usando a configuração que adicionaremos ao arquivo application.properties
. Abra o arquivo src/main/resource/application.properties
e adicione:
# Configure the outgoing RabbitMQ exchange `quote-requests`
mp.messaging.outgoing.quote-requests.connector=smallrye-rabbitmq
mp.messaging.outgoing.quote-requests.exchange.name=quote-requests
Tudo o que precisamos especificar é o conector smallrye-rabbitmq
. Por padrão, as mensagens reativas mapeiam o nome do canal quote-requests
para o mesmo nome de troca do RabbitMQ.
Processamento de pedidos de cotação
Agora vamos consumir a solicitação de cotação e fornecer um preço. Dentro do projeto processor
, localize o arquivo src/main/java/org/acme/rabbitmq/processor/QuoteProcessor.java
e adicione o seguinte:
package org.acme.rabbitmq.processor;
import java.util.Random;
import jakarta.enterprise.context.ApplicationScoped;
import org.acme.rabbitmq.model.Quote;
import org.eclipse.microprofile.reactive.messaging.Incoming;
import org.eclipse.microprofile.reactive.messaging.Outgoing;
import io.smallrye.reactive.messaging.annotations.Blocking;
/**
* A bean consuming data from the "quote-requests" RabbitMQ queue and giving out a random quote.
* The result is pushed to the "quotes" RabbitMQ exchange.
*/
@ApplicationScoped
public class QuoteProcessor {
private Random random = new Random();
@Incoming("requests") (1)
@Outgoing("quotes") (2)
@Blocking (3)
public Quote process(String quoteRequest) throws InterruptedException {
// simulate some hard-working task
Thread.sleep(1000);
return new Quote(quoteRequest, random.nextInt(100));
}
}
1 | Indica que o método consome os itens do canal quote-requests |
2 | Indica que os objetos devolvidos pelo método são enviados para o canal quotes |
3 | Indica que o processamento está a bloquear e não pode ser executado na thread do chamador. |
O método process
é chamado para cada mensagem RabbitMQ da fila quote-requests
, e enviará um objeto Quote
para a fila quotes
.
Como no exemplo anterior, precisamos configurar os conectores no arquivo application.properties
. Abra o arquivo src/main/resources/application.properties
e adicione:
# Configure the incoming RabbitMQ queue `quote-requests`
mp.messaging.incoming.requests.connector=smallrye-rabbitmq
mp.messaging.incoming.requests.queue.name=quote-requests
mp.messaging.incoming.requests.exchange.name=quote-requests
# Configure the outgoing RabbitMQ exchange `quotes`
mp.messaging.outgoing.quotes.connector=smallrye-rabbitmq
mp.messaging.outgoing.quotes.exchange.name=quotes
Observe que, neste caso, temos uma configuração de conector de entrada e uma de saída, cada uma com um nome distinto. As propriedades de configuração são estruturadas da seguinte forma:
mp.messaging.[outgoing|incoming].{channel-name}.property=value
O segmento channel-name
deve corresponder ao valor definido na anotação @Incoming
e @Outgoing
:
-
quote-requests
→ Fila RabbitMQ a partir da qual lemos os pedidos de cotação -
quotes
→ Bolsa RabbitMQ na qual escrevemos as cotações
Recebendo cotações
Voltemos ao nosso projeto producer
. Vamos modificar o QuotesResource
para consumir cotações e vinculá-lo a um endpoint HTTP para enviar eventos aos clientes:
import io.smallrye.mutiny.Multi;
//...
@Channel("quotes") Multi<Quote> quotes; (1)
/**
* Endpoint retrieving the "quotes" queue and sending the items to a server sent event.
*/
@GET
@Produces(MediaType.SERVER_SENT_EVENTS) (2)
public Multi<Quote> stream() {
return quotes; (3)
}
1 | Injecta o canal quotes utilizando o qualificador @Channel |
2 | Indica que o conteúdo é enviado utilizando Server Sent Events |
3 | Devolve o fluxo (Reactive Stream) |
Novamente, precisamos configurar o canal de entrada quotes
dentro do projeto producer
. Adicione o seguinte no arquivo application.properties
:
# Configure the outgoing `quote-requests` queue
mp.messaging.outgoing.quote-requests.connector=smallrye-rabbitmq
# Configure the incoming `quotes` queue
mp.messaging.incoming.quotes.connector=smallrye-rabbitmq
A página HTML
Toque final, a página HTML que lê os preços convertidos utilizando SSE.
Criar no projeto producer
o ficheiro src/main/resources/META-INF/resources/quotes.html
, com o seguinte conteúdo:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Quotes</title>
<link rel="stylesheet" type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/patternfly/3.24.0/css/patternfly.min.css">
<link rel="stylesheet" type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/patternfly/3.24.0/css/patternfly-additions.min.css">
</head>
<body>
<div class="container">
<div class="card">
<div class="card-body">
<h2 class="card-title">Quotes</h2>
<button class="btn btn-info" id="request-quote">Request Quote</button>
<div class="quotes"></div>
</div>
</div>
</div>
</body>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$("#request-quote").click((event) => {
fetch("/quotes/request", {method: "POST"})
.then(res => res.text())
.then(qid => {
var row = $(`<h4 class='col-md-12' id='${qid}'>Quote # <i>${qid}</i> | <strong>Pending</strong></h4>`);
$(".quotes").append(row);
});
});
var source = new EventSource("/quotes");
source.onmessage = (event) => {
var json = JSON.parse(event.data);
$(`#${json.id}`).html(function(index, html) {
return html.replace("Pending", `\$\xA0${json.price}`);
});
};
</script>
</html>
Nada de espetacular aqui. A cada citação recebida, a página é atualizada.
Executando a aplicação
Você só precisa executar ambas as aplicações utilizando:
mvn -f rabbitmq-quickstart-producer quarkus:dev
E, em um terminal separado:
mvn -f rabbitmq-quickstart-processor quarkus:dev
O Quarkus inicia um broker RabbitMQ automaticamente, configura o aplicativo e compartilha a instância do broker entre diferentes aplicativos. Consulte Dev Services for RabbitMQ] para obter mais detalhes.
Abra http://localhost:8080/quotes.html
no seu browser e peça algumas cotações clicando no botão.
Execução em modo JVM ou nativo
Quando não estiver em execução no modo de desenvolvimento ou teste, você precisará iniciar o corretor RabbitMQ. Você pode seguir as instruções do RabbitMQ Docker website ou criar um arquivo docker-compose.yaml
com o seguinte conteúdo:
version: '2'
services:
rabbit:
image: rabbitmq:3.12-management
ports:
- "5672:5672"
networks:
- rabbitmq-quickstart-network
producer:
image: quarkus-quickstarts/rabbitmq-quickstart-producer:1.0-${QUARKUS_MODE:-jvm}
build:
context: rabbitmq-quickstart-producer
dockerfile: src/main/docker/Dockerfile.${QUARKUS_MODE:-jvm}
environment:
RABBITMQ_HOST: rabbit
RABBITMQ_PORT: 5672
ports:
- "8080:8080"
networks:
- rabbitmq-quickstart-network
processor:
image: quarkus-quickstarts/rabbitmq-quickstart-processor:1.0-${QUARKUS_MODE:-jvm}
build:
context: rabbitmq-quickstart-processor
dockerfile: src/main/docker/Dockerfile.${QUARKUS_MODE:-jvm}
environment:
RABBITMQ_HOST: rabbit
RABBITMQ_PORT: 5672
networks:
- rabbitmq-quickstart-network
networks:
rabbitmq-quickstart-network:
name: rabbitmq-quickstart
Observe como o local do broker RabbitMQ está configurado. As propriedades rabbitmq-host
e rabbitmq-port
(variáveis de ambiente AMQP_HOST
e AMQP_PORT
) configuram o local.
Primeiro, certifique-se de que parou as aplicações e construa ambas as aplicações no modo JVM com:
mvn -f rabbitmq-quickstart-producer clean package
mvn -f rabbitmq-quickstart-processor clean package
Depois de empacotado, execute docker compose up --build
. A interface do usuário será exibida em http://localhost:8080/quotes.html
Para executar as suas aplicações como nativas, primeiro precisaremos construir os executáveis nativos:
mvn -f rabbitmq-quickstart-producer package -Dnative -Dquarkus.native.container-build=true
mvn -f rabbitmq-quickstart-processor package -Dnative -Dquarkus.native.container-build=true
O site -Dquarkus.native.container-build=true
instrui o Quarkus a criar executáveis nativos do Linux de 64 bits, que podem ser executados dentro de contêineres. Em seguida, execute o sistema usando:
export QUARKUS_MODE=native
docker compose up --build
Tal como anteriormente, a interface do usuário será exibida em http://localhost:8080/quotes.html
Indo mais longe
Este guia mostrou como você pode interagir com o RabbitMQ usando o Quarkus. Ele utiliza o SmallRye Reactive Messaging para criar aplicativos de streaming de dados.
Se você já utilizou o Kafka, percebeu que o código é o mesmo. A única diferença é a configuração do conector e o mapeamento JSON.