Using JMS

本指南演示了 Quarkus 应用程序如何通过 Apache Qpid JMS AMQP 客户端或 Apache ActiveMQ Artemis JMS 客户端来使用 JMS 消息传递。

This guide demonstrates how your Quarkus application can use JMS messaging via the Apache Qpid JMS AMQP client, or alternatively the Apache ActiveMQ Artemis JMS client. Unresolved directive in jms.adoc - include::{includes}/extension-status.adoc[]

Prerequisites

include::{includes}/prerequisites.adoc[]* 正在运行的 Artemis 服务器或用于启动一个 Artemis 服务器的 Docker

Unresolved directive in jms.adoc - include::{includes}/prerequisites.adoc[] * A running Artemis server, or Docker to start one

Architecture

在此指南中,我们将在一个组件中生成(随机)价格。这些价格使用 JMS 客户端写入队列 (prices)。另一个组件从 `prices`队列中读取内容并存储最新价格。可以从浏览器中使用 Jakarta REST 资源中的获取按钮获取数据。

In this guide, we are going to generate (random) prices in one component. These prices are written to a queue (prices) using a JMS client. Another component reads from the prices queue and stores the latest price. The data can be fetched from a browser using a fetch button from a Jakarta REST resource.

可以在下面按照详细说明操作,使用 Apache Qpid JMS AMQP 客户端来使用指南,或者在一些不同的配置中 (detailed later) 指定 Apache ActiveMQ Artemis JMS 客户端。

The guide can be used either via the Apache Qpid JMS AMQP client as detailed immediately below, or alternatively with the Apache ActiveMQ Artemis JMS client given some different configuration as artemis-jms.

Qpid JMS - AMQP

在以下详细步骤中,我们将通过 Quarkus Qpid JMS extension使用 Apache Qpid JMS客户端。Qpid JMS 将 AMQP 1.0 ISO 标准用作其传输协议,使其可以与各种 AMQP 1.0 服务器和服务(如 ActiveMQ Artemis、ActiveMQ 5、Qpid Broker-J、Qpid Dispatch 路由器、Azure 服务总线等)一起使用。

In the detailed steps below we will use the Apache Qpid JMS client via the Quarkus Qpid JMS extension. Qpid JMS uses the AMQP 1.0 ISO standard as its wire protocol, allowing it to be used with a variety of AMQP 1.0 servers and services such as ActiveMQ Artemis, ActiveMQ 5, Qpid Broker-J, Qpid Dispatch router, Azure Service Bus, and more.

Solution

我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.

克隆 Git 存储库: git clone [role="bare"]https://github.com/amqphub/quarkus-qpid-jms-quickstart.git,或下载 archive.

Clone the Git repository: git clone [role="bare"]https://github.com/amqphub/quarkus-qpid-jms-quickstart.git, or download an archive.

Creating the Maven Project

首先,我们需要一个新项目。使用以下命令创建一个新项目:

First, we need a new project. Create a new project with the following command:

Unresolved directive in jms.adoc - include::{includes}/devtools/create-app.adoc[]

此命令生成导入 quarkus-qpid-jms 扩展的新项目:

This command generates a new project importing the quarkus-qpid-jms extension:

pom.xml
<dependency>
    <groupId>org.amqphub.quarkus</groupId>
    <artifactId>quarkus-qpid-jms</artifactId>
</dependency>
build.gradle
implementation("org.amqphub.quarkus:quarkus-qpid-jms")

Starting the broker

接下来,我们需要一个 AMQP 代理。在此情况下,我们将使用 Apache ActiveMQ Artemis 服务器。您可以按照 Apache Artemis website中的说明操作,或使用 ArtemisCloud容器映像通过 docker 启动代理:

Then, we need an AMQP broker. In this case we will use an Apache ActiveMQ Artemis server. You can follow the instructions from the Apache Artemis website or start a broker via docker using the ArtemisCloud container image:

docker run -it --rm -p 8161:8161 -p 61616:61616 -p 5672:5672 -e AMQ_USER=quarkus -e AMQ_PASSWORD=quarkus quay.io/artemiscloud/activemq-artemis-broker:1.0.25

The price producer

创建 `src/main/java/org/acme/jms/PriceProducer.java`文件,其内容如下:

Create the src/main/java/org/acme/jms/PriceProducer.java file, with the following content:

package org.acme.jms;

import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.jms.ConnectionFactory;
import jakarta.jms.JMSContext;

import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;

/**
 * A bean producing random prices every 5 seconds and sending them to the prices JMS queue.
 */
@ApplicationScoped
public class PriceProducer implements Runnable {

    @Inject
    ConnectionFactory connectionFactory;

    private final Random random = new Random();
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

    void onStart(@Observes StartupEvent ev) {
        scheduler.scheduleWithFixedDelay(this, 0L, 5L, TimeUnit.SECONDS);
    }

    void onStop(@Observes ShutdownEvent ev) {
        scheduler.shutdown();
    }

    @Override
    public void run() {
        try (JMSContext context = connectionFactory.createContext(JMSContext.AUTO_ACKNOWLEDGE)) {
            context.createProducer().send(context.createQueue("prices"), Integer.toString(random.nextInt(100)));
        }
    }
}

The price consumer

价格使用者从 JMS 中读取价格并存储最后一个价格。使用以下内容创建 `src/main/java/org/acme/jms/PriceConsumer.java`文件:

The price consumer reads the prices from JMS, and stores the last one. Create the src/main/java/org/acme/jms/PriceConsumer.java file with the following content:

package org.acme.jms;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.jms.ConnectionFactory;
import jakarta.jms.JMSConsumer;
import jakarta.jms.JMSContext;
import jakarta.jms.JMSException;
import jakarta.jms.Message;

import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;

/**
 * A bean consuming prices from the JMS queue.
 */
@ApplicationScoped
public class PriceConsumer implements Runnable {

    @Inject
    ConnectionFactory connectionFactory;

    private final ExecutorService scheduler = Executors.newSingleThreadExecutor();

    private volatile String lastPrice;

    public String getLastPrice() {
        return lastPrice;
    }

    void onStart(@Observes StartupEvent ev) {
        scheduler.submit(this);
    }

    void onStop(@Observes ShutdownEvent ev) {
        scheduler.shutdown();
    }

    @Override
    public void run() {
        try (JMSContext context = connectionFactory.createContext(JMSContext.AUTO_ACKNOWLEDGE)) {
            JMSConsumer consumer = context.createConsumer(context.createQueue("prices"));
            while (true) {
                Message message = consumer.receive();
                if (message == null) return;
                lastPrice = message.getBody(String.class);
            }
        } catch (JMSException e) {
            throw new RuntimeException(e);
        }
    }
}

The price resource

最后,让我们创建一个简单的 Jakarta REST 资源来显示最后一个价格。使用以下内容创建 `src/main/java/org/acme/jms/PriceResource.java`文件:

Finally, let’s create a simple Jakarta REST resource to show the last price. Create the src/main/java/org/acme/jms/PriceResource.java file with the following content:

package org.acme.jms;

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

/**
 * A simple resource showing the last price.
 */
@Path("/prices")
public class PriceResource {

    @Inject
    PriceConsumer consumer;

    @GET
    @Path("last")
    @Produces(MediaType.TEXT_PLAIN)
    public String last() {
        return consumer.getLastPrice();
    }
}

The HTML page

最后一步,使用 SSE 读取已转换价格的 HTML 页面。

Final touch, the HTML page reading the converted prices using SSE.

创建 `src/main/resources/META-INF/resources/prices.html`文件,其内容如下:

Create the src/main/resources/META-INF/resources/prices.html file, with the following content:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Prices</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">

    <h2>Last price</h2>
    <div class="row">
    <p class="col-md-12"><button id="fetch">Fetch</button>The last price is <strong><span id="content">N/A</span> €</strong>.</p>
    </div>
</div>
</body>
<script>
    document.getElementById("fetch").addEventListener("click", function() {
        fetch("/prices/last").then(function (response) {
            response.text().then(function (text) {
                document.getElementById("content").textContent = text;
            })
        })
    })
</script>
</html>

没有特别之处。在每次读取时,它都会更新页面。

Nothing spectacular here. On each fetch, it updates the page.

Configure the Qpid JMS properties

我们需要在注入 ConnectionFactory 时配置扩展使用的 Qpid JMS 属性。

We need to configure the Qpid JMS properties used by the extension when injecting the ConnectionFactory.

它是在 `src/main/resources/application.properties`文件中完成的。

This is done in the src/main/resources/application.properties file.

# Configures the Qpid JMS properties.
quarkus.qpid-jms.url=amqp://localhost:5672
quarkus.qpid-jms.username=quarkus
quarkus.qpid-jms.password=quarkus

有关配置的更多详细信息,请参阅 Quarkus Qpid JMS 文档中。

More detail about the configuration are available in the Quarkus Qpid JMS documentation.

Get it running

如果您按照了说明,那么应该已经启动了 Artemis 服务器。接下来,您只需使用以下命令运行应用程序:

If you followed the instructions, you should have the Artemis server running. Then, you just need to run the application using:

Unresolved directive in jms.adoc - include::{includes}/devtools/dev.adoc[]

在浏览器中打开 http://localhost:8080/prices.html

Open http://localhost:8080/prices.html in your browser.

Running Native

您可以通过以下命令构建原生可执行文件:

You can build the native executable with:

Unresolved directive in jms.adoc - include::{includes}/devtools/build-native.adoc[]

或者,如果您没有安装 GraalVM,则可以使用 Docker 来构建原生可执行文件:

Or, if you don’t have GraalVM installed, you can instead use Docker to build the native executable using:

Unresolved directive in jms.adoc - include::{includes}/devtools/build-native-container.adoc[]

然后通过以下命令运行:

and then run with:

./target/jms-quickstart-1.0.0-SNAPSHOT-runner

在浏览器中打开 http://localhost:8080/prices.html

Open http://localhost:8080/prices.html in your browser.

Artemis JMS

上述步骤使用 Qpid JMS AMQP 客户端进行了详细说明,但是指南也可以与 Artemis JMS 客户端一起使用。许多单个步骤与之前 detailed above for Qpid JMS 中的步骤完全相同。各个组件代码也是相同的。唯一的区别在于初始项目创建的依赖项,以及所使用的配置属性。这些更改将在下面进行详细说明,应当替换上述序列中的等效步骤。

The above steps detailed using the Qpid JMS AMQP client, however the guide can also be used with the Artemis JMS client. Many of the individual steps are exactly as previously qpid-jms-amqp. The individual component code is the same. The only differences are in the dependency for the initial project creation, and the configuration properties used. These changes are detailed below and should be substituted for the equivalent step during the sequence above.

Solution

您可以直接转到完成的示例。

You can go right to the completed example.

克隆 Git 存储库: git clone {quickstarts-clone-url},或下载 {quickstarts-archive-url}[存档]。

Clone the Git repository: git clone {quickstarts-clone-url}, or download an {quickstarts-archive-url}[archive].

Artemis JMS 解决方案位于 jms-quickstart directory 中。

The Artemis JMS solution is located in the jms-quickstart directory.

Creating the Maven Project

使用以下命令创建一个新项目:

Create a new project with the following command:

Unresolved directive in jms.adoc - include::{includes}/devtools/create-app.adoc[]

这将创建一个导入 quarkus-artemis-jms 扩展的新项目:

This creates a new project importing the quarkus-artemis-jms extension:

pom.xml
<dependency>
    <groupId>io.quarkiverse.artemis</groupId>
    <artifactId>quarkus-artemis-jms</artifactId>
</dependency>
build.gradle
implementation("io.quarkiverse.artemis:quarkus-artemis-jms")

创建完项目后,您可以从上面详细步骤中的 Starting the broker 处继续开始,一直进行到配置 application.properties 文件,此时您应使用以下 Artemis 配置作为替代。

With the project created, you can resume from Starting the broker in the detailed steps above and proceed until configuring the application.properties file, when you should use the Artemis configuration below instead.

Configure the Artemis properties

我们需要配置 Artemis 连接属性。这将在 src/main/resources/application.properties 文件中完成。

We need to configure the Artemis connection properties. This is done in the src/main/resources/application.properties file.

# Configures the Artemis properties.
quarkus.artemis.url=tcp://localhost:61616
quarkus.artemis.username=quarkus
quarkus.artemis.password=quarkus

配置完 Artemis 属性后,您可以在 Get it running 处继续上述步骤。

With the Artemis properties configured, you can resume the steps above from Get it running.

Configuration Reference

要详细了解如何配置 Artemis JMS 客户端,请参阅 the documentation of the extension

To know more about how to configure the Artemis JMS client, have a look at the documentation of the extension.