The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

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.

Architecture

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 .

Producer App UI

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:

CLI
quarkus create app org.acme:rabbitmq-quickstart-producer \
    --extension='messaging-rabbitmq,rest-jackson' \
    --no-code

Para criar um projeto Gradle, adicione a opção --gradle ou --gradle-kotlin-dsl.

Para obter mais informações sobre como instalar e usar a CLI do Quarkus, consulte o guia Quarkus CLI.

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.17.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=rabbitmq-quickstart-producer \
    -Dextensions='messaging-rabbitmq,rest-jackson' \
    -DnoCode

Para criar um projeto Gradle, adicione a opção '-DbuildTool=gradle' ou '-DbuildTool=gradle-kotlin-dsl'.

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:

  1. O conector RabbitMQ de mensagens reactivas

  2. 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 messaging-rabbitmq extension to your project by running the following command in your project base directory:

CLI
quarkus extension add messaging-rabbitmq
Maven
./mvnw quarkus:add-extension -Dextensions='messaging-rabbitmq'
Gradle
./gradlew addExtension --extensions='messaging-rabbitmq'

Isso adicionará o seguinte ao seu arquivo pom.xml:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-messaging-rabbitmq</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-messaging-rabbitmq")

Para criar o projeto do processor, a partir do mesmo diretório, execute:

CLI
quarkus create app org.acme:rabbitmq-quickstart-processor \
    --extension='messaging-rabbitmq' \
    --no-code

Para criar um projeto Gradle, adicione a opção --gradle ou --gradle-kotlin-dsl.

Para obter mais informações sobre como instalar e usar a CLI do Quarkus, consulte o guia Quarkus CLI.

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.17.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=rabbitmq-quickstart-processor \
    -Dextensions='messaging-rabbitmq' \
    -DnoCode

Para criar um projeto Gradle, adicione a opção '-DbuildTool=gradle' ou '-DbuildTool=gradle-kotlin-dsl'.

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 @RegisterForReflection annotation instructs Quarkus to keep the class, its fields, and methods when creating a native executable. This is crucial when we later run our applications as native executables within containers. Without this annotation, the native compilation process would discard the fields and methods during the dead-code elimination phase, which would lead to runtime errors. More details about the @RegisterForReflection annotation can be found on the native application tips page.

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.

Conteúdo Relacionado