package com.example.messagingrabbitmq;
import java.util.concurrent.CountDownLatch;
import org.springframework.stereotype.Component;
@Component
public class Receiver {
private CountDownLatch latch = new CountDownLatch(1);
public void receiveMessage(String message) {
System.out.println("Received <" + message + ">");
latch.countDown();
}
public CountDownLatch getLatch() {
return latch;
}
}
使用 RabbitMQ 进行消息传递
本指南将引导您完成创建一个发布和订阅 RabbitMQ AMQP 服务器的 Spring Boot 应用程序的过程。
您将构建什么
您将构建一个应用程序,该应用程序使用 Spring AMQP 的 RabbitTemplate 发布消息,并使用 MessageListenerAdapter 在 POJO 上订阅消息。
你需要什么
-
大约 15 分钟
-
一个喜欢的文本编辑器或 IDE
-
Java 17 或更高版本
如何完成本指南
要在您的本地环境中查看最终结果,您可以执行以下操作之一
-
下载并解压缩此指南的源代码存储库
-
使用 Git 克隆存储库:
git clone https://github.com/spring-guides/gs-messaging-rabbitmq.git -
Fork 仓库,这样您就可以通过提交拉取请求来请求对本指南的更改
设置 RabbitMQ Broker
在构建消息应用程序之前,您需要设置一个服务器来处理消息的接收和发送。本指南假定您使用 Spring Boot Docker Compose 支持。这种方法的先决条件是您的开发机器已安装 Docker 环境,例如 Docker Desktop。添加一个 spring-boot-docker-compose 依赖项,它执行以下操作:
-
在您的工作目录中搜索
compose.yml和其他常见的 compose 文件名 -
使用发现的
compose.yml调用docker compose up -
为每个支持的容器创建服务连接 bean
-
应用程序关闭时调用
docker compose stop
要使用 Docker Compose 支持,您只需按照本指南操作即可。根据您引入的依赖项,Spring Boot 会找到正确的 compose.yml 文件并在您运行应用程序时启动您的 Docker 容器。
如果您选择自己运行 RabbitMQ 服务器而不是使用 Spring Boot Docker Compose 支持,您有几种选择:
-
下载服务器并手动运行它
-
如果您使用 Mac,请通过 Homebrew 安装
-
使用
docker-compose up手动运行compose.yaml文件
如果您选择任何这些替代方法,您应该从 Maven 或 Gradle 构建文件中删除 spring-boot-docker-compose 依赖项。您还需要将配置添加到 application.properties 文件中,如 准备构建应用程序部分所述。如前所述,本指南假定您在 Spring Boot 中使用 Docker Compose 支持,因此此时不需要对 application.properties 进行其他更改。
从 Spring Initializr 开始
您可以使用这个 预初始化项目 并单击 Generate 来下载 ZIP 文件。此项目已配置为符合本指南中的示例。
手动初始化项目
-
导航到 start.spring.io。此服务会拉取应用程序所需的所有依赖项,并为您完成大部分设置。
-
选择 Gradle 或 Maven 以及您想要使用的语言。本指南假设您选择了 Java。
-
点击 Dependencies 并选择 Spring for RabbitMQ 和 Docker Compose Support。
-
单击生成。
-
下载生成的 ZIP 文件,它是一个已根据您的选择配置的应用程序存档。
| 如果您的 IDE 集成了 Spring Initializr,您可以从 IDE 中完成此过程。 |
创建 RabbitMQ 消息接收器
对于任何基于消息的应用程序,您都需要创建一个响应已发布消息的接收器。以下列表(来自 src/main/java/com/example/messagingrabbitmq/Receiver.java)显示了如何执行此操作:
Receiver 是一个 POJO,它定义了一个用于接收消息的方法。当您注册它以接收消息时,您可以随意命名。
为方便起见,此 POJO 还包含一个 CountDownLatch。这允许它发出消息已接收的信号。在生产应用程序中,您不太可能实现这一点。 |
注册监听器并发送消息
Spring AMQP 的 RabbitTemplate 提供了使用 RabbitMQ 发送和接收消息所需的一切。但是,您需要:
-
配置消息监听器容器。
-
声明队列、交换器以及它们之间的绑定。
-
配置一个组件来发送一些消息以测试监听器。
| Spring Boot 会自动创建连接工厂和 RabbitTemplate,从而减少您需要编写的代码量。 |
您将使用 RabbitTemplate 发送消息,并将 Receiver 注册到消息监听器容器以接收消息。连接工厂驱动两者,使它们能够连接到 RabbitMQ 服务器。以下列表(来自 src/main/java/com/example/messagingrabbitmq/MessagingRabbitmqApplication.java)显示了如何创建应用程序类:
package com.example.messagingrabbitmq;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MessagingRabbitmqApplication {
static final String topicExchangeName = "spring-boot-exchange";
static final String queueName = "spring-boot";
@Bean
Queue queue() {
return new Queue(queueName, false);
}
@Bean
TopicExchange exchange() {
return new TopicExchange(topicExchangeName);
}
@Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("foo.bar.#");
}
@Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(queueName);
container.setMessageListener(listenerAdapter);
return container;
}
@Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(MessagingRabbitmqApplication.class, args).close();
}
}
@SpringBootApplication 注解提供了许多好处,如 参考文档中所述。
在 listenerAdapter() 方法中定义的 Bean 将作为消息监听器注册到容器(在 container() 中定义)。它监听 spring-boot 队列上的消息。由于 Receiver 类是一个 POJO,它需要被包装在 MessageListenerAdapter 中,您可以在其中指定它调用 receiveMessage。
| JMS 队列和 AMQP 队列具有不同的语义。例如,JMS 将排队的消息发送给一个消费者。虽然 AMQP 队列执行相同的操作,但 AMQP 生产者不直接将消息发送到队列。相反,消息被发送到一个交换器,该交换器可以发送到单个队列或分发到多个队列,从而模拟 JMS 主题的概念。 |
消息监听器容器和接收器 Bean 是监听消息所需的一切。要发送消息,您还需要一个 Rabbit 模板。
queue() 方法创建一个 AMQP 队列。exchange() 方法创建一个主题交换器。binding() 方法将两者绑定在一起,定义 RabbitTemplate 发布到交换器时发生的行为。
Spring AMQP 要求 Queue、TopicExchange 和 Binding 作为顶级 Spring Bean 声明,以便正确设置。 |
在这种情况下,我们使用主题交换器,并将队列绑定到路由键 foo.bar.#,这意味着任何以 foo.bar. 开头的路由键发送的消息都将被路由到队列。
发送测试消息
在此示例中,测试消息由 CommandLineRunner 发送,它还等待接收器中的闩锁并关闭应用程序上下文。以下列表(来自 src/main/java/com.example.messagingrabbitmq/Runner.java)显示了它的工作原理:
package com.example.messagingrabbitmq;
import java.util.concurrent.TimeUnit;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Runner implements CommandLineRunner {
private final RabbitTemplate rabbitTemplate;
private final Receiver receiver;
public Runner(Receiver receiver, RabbitTemplate rabbitTemplate) {
this.receiver = receiver;
this.rabbitTemplate = rabbitTemplate;
}
@Override
public void run(String... args) throws Exception {
System.out.println("Sending message...");
rabbitTemplate.convertAndSend(MessagingRabbitmqApplication.topicExchangeName, "foo.bar.baz", "Hello from RabbitMQ!");
receiver.getLatch().await(10000, TimeUnit.MILLISECONDS);
}
}
请注意,模板会将消息以路由键 foo.bar.baz 路由到交换器,该路由键与绑定匹配。
在测试中,您可以模拟运行程序,以便可以独立测试接收器。
运行应用程序
main() 方法通过创建 Spring 应用程序上下文来启动该过程。这会启动消息监听器容器,然后开始监听消息。存在一个 Runner Bean,它随后会自动运行。它从应用程序上下文中检索 RabbitTemplate,并在 spring-boot 队列上发送一条 Hello from RabbitMQ! 消息。最后,它关闭 Spring 应用程序上下文,应用程序结束。
您可以通过 IDE 运行 main 方法。请注意,如果您从解决方案存储库克隆了项目,您的 IDE 可能会在错误的位置查找 compose.yaml 文件。您可以配置您的 IDE 以查找正确的位置,或者使用命令行运行应用程序。./gradlew bootRun 和 ./mvnw spring-boot:run 命令将启动应用程序并自动找到 compose.yaml 文件。
准备构建应用程序
要运行代码而不使用 Spring Boot Docker Compose 支持,您需要本地运行一个 RabbitMQ 版本来连接。为此,您可以使用 Docker Compose,但必须先对 compose.yaml 文件进行两项更改。首先,将 compose.yaml 中的 ports 条目修改为 '5672:5672'。其次,添加一个 container_name。
compose.yaml 现在应该是:
services:
rabbitmq:
container_name: 'guide-rabbit'
image: 'rabbitmq:latest'
environment:
- 'RABBITMQ_DEFAULT_PASS=secret'
- 'RABBITMQ_DEFAULT_USER=myuser'
ports:
- '5672:5672'
您现在可以运行 docker-compose up 来启动 RabbitMQ 服务。现在您应该有一个外部 RabbitMQ 服务器已准备好接受请求。
此外,您还需要告诉 Spring 如何连接到 RabbitMQ 服务器(这在使用 Spring Boot Docker Compose 支持时会自动处理)。将以下代码添加到 src/main/resources 中的新 application.properties 文件中:
spring.rabbitmq.password=secret spring.rabbitmq.username=myuser
构建应用程序
本节描述了运行本指南的不同方法
无论您选择如何运行应用程序,输出都应该相同。
要运行应用程序,您可以将应用程序打包为可执行 JAR。./gradlew clean build 命令将应用程序编译为可执行 JAR。然后,您可以使用 java -jar build/libs/messaging-rabbitmq-0.0.1-SNAPSHOT.jar 命令来运行 JAR。
或者,如果您有可用的 Docker 环境,您可以使用 Maven 或 Gradle 插件直接创建 Docker 镜像,使用 buildpacks。借助 Cloud Native Buildpacks,您可以创建可在任何地方运行的 Docker 兼容镜像。Spring Boot 直接为 Maven 和 Gradle 提供了 buildpack 支持。这意味着您只需键入一个命令即可快速获得一个合理的镜像到本地运行的 Docker 守护进程。要使用 Cloud Native Buildpacks 创建 Docker 镜像,请运行 ./gradlew bootBuildImage 命令。在启用了 Docker 环境的情况下,您可以使用 docker run --network container:guide-rabbit docker.io/library/messaging-rabbitmq:0.0.1-SNAPSHOT 命令来运行应用程序。
--network 标志告诉 Docker 将我们的 guide 容器连接到我们的外部容器正在使用的现有网络。您可以在 Docker 文档中找到更多信息。 |
无论您选择如何构建和运行应用程序,都应该看到以下输出:
Sending message...
Received <Hello from RabbitMQ!>
总结
恭喜!您已经使用 Spring 和 RabbitMQ 开发了一个简单的发布-订阅应用程序。您可以使用 Spring and RabbitMQ 完成比本指南所涵盖的更多工作,但这应该为您提供一个良好的开端。