本次我们讲述使用Springboot来操作 RabbitMQ 发布和订阅消息。消息队列在实际开发中经常用到,主要做了处理大规模数据,及模块解耦。而RabbitMQ是目前最热的消息队列之一,本例你可以学到RabbitMQ安装和使用。
我利用业余时间,翻译了Spring官网的例子,方便中文不好的同学,将陆续发到CSDN上,欢迎大家关注,也可以上我个人BLOG:itmanclub.com
,上面有已经翻译过的。
正文:
** 程序结构**
└── src
└── main
└── java
└── hello
** pom.xml文件**
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-messaging-rabbitmq</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Spring Boot将会你做如下的事:
- 将 classpath 里面所有用到的jar包构建成一个可执行的 JAR 文件,方便执行你的程序
- 搜索public static void main()方法并且将它当作可执行类
- 根据springboot版本,去查找相应的依赖类版本,当然你可以定义其它版本。
** 创建一个RabbitMQ代理服务器**
在构建消息传递应用程序之前,需要设置处理接收和发送消息的Redis服务器。
RabbitMQ是AMQP服务器。服务器可从https://www.rabbitmq.com/download.html
免费获得。
解包服务器并使用默认设置启动它。
rabbitmq-server
您应该看到这样的消息:
RabbitMQ 3.1.3. Copyright (C) 2007-2013 VMware, Inc.
## ## Licensed under the MPL. See https://www.rabbitmq.com/
## ##
########## Logs: /usr/local/var/log/rabbitmq/rabbit@localhost.log
###### ## /usr/local/var/log/rabbitmq/rabbit@localhost-sasl.log
##########
Starting broker... completed with 6 plugins.
如果有docker compose在本地运行,也可以使用docker compose快速启动RabbitMQ服务器。在Github的“complete”项目的根目录中有一个docker-compose.yml,配置很简单:
docker-compose.yml
rabbitmq:
image: rabbitmq:management
ports:
- "5672:5672"
- "15672:15672"
使用当前目录中的这个文件,您可以使RabbitMQ运行在docker compose容器中。
** 创建一个RabbitMQ消息接收器**
对于任何基于消息的应用程序,您需要创建一个消息接收器。
src/main/java/hello/Receiver.java
package hello;
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;
}
}
这个接收器是一个简单的POJO,它定义了接收消息的方法。当您注册它来接收消息时,您可以随意命名它。
为了方便起见,这个pojo还有一个CountDownLatch(倒计时)。这允许它发出信号来表明消息被接收了。在生产应用环境中,不用这样做的。
** 注册侦听器并发送消息**
Spring AMQP的RabbitTemplate提供了使用RabbitMQ发送和接收消息所需的一切。具体来说,您需要配置:
- 消息侦听器容器
- 声明队列、交换和它们之间的绑定
- 用于发送一些消息以测试侦听器的组件
- SpringBoot会自动创建一个连接工厂和一个RabbitTemplate,从而减少代码量。
您将使用RabbitTemplate发送消息,并且将在消息侦听器容器中注册一个接收者来接收消息。连接工厂类将驱动这两者,允许它们连接到RabbitMQ服务器。
src/main/java/hello/Application.java
package hello;
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 Application {
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(Application.class, args).close();
}
}
@SpringBootApplication包含如下注解:
- @Configuration 将类标记为应用程序上下文的bean定义源。
- @EnableAutoConfiguration 告诉SpringBoot根据类路径设置、其他bean和各种属性设置开始添加bean。
- @ComponentScan 告诉Spring在hello包中查找其他组件、配置和服务。
您注意到没有一行XML吗?也没有web.xml文件。这个Web应用程序是100%纯Java,您不必麻烦的基础配置。
listenerAdapter()方法中定义的bean在container()中定义的容器中被注册为消息侦听器。它将监听“spring-boot”队列中的消息。
因为receiver类是一个POJO,所以需要将其包装在MessageListenerAdapter中,并在其中指定它来调用receiveMessage。
JMS队列和AMQP队列具有不同的含义。例如,JMS只向一个使用者发送排队的消息。虽然AMQP队列执行相同的操作,但AMQP生产者不会直接向队列发送消息。
相反,消息被发送到一个交换区,那里可以通往一个队列,或者扇出到多个队列,从而模仿JMS主题的概念。有关更多信息,请参见AMQP信息。
消息侦听器容器和接收器bean都是来侦听消息的,要发送消息,还需要一个Rabbit模板。
queue()方法创建AMQP队列。exchange()方法创建主题交换。binding() 方法将这两个绑定在一起,定义当RabbitTemplate发布到交换时发生的行为。
Spring AMQP要求将队列、topiceExchange和绑定等声明为顶级Spring Beans,以便正确设置。
在这种情况下,我们使用主题交换、队列等绑定到路由键foo.bar.#,这意味着任何以foo.bar开头的消息将发送到队列。
** 发送测试信息**
测试消息由commandlinerunner发送,它还等待接收器中的latch并关闭应用程序上下文:
src/main/java/hello/Runner.java
package hello;
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(Application.topicExchangeName, "foo.bar.baz", "Hello from RabbitMQ!");
receiver.getLatch().await(10000, TimeUnit.MILLISECONDS);
}
}
注意,模板将消息路由到交换,路由键为foo.bar.baz,与绑定匹配。
测试中可以模拟出运行程序,这样就可以对接收器进行隔离测试。
** 运行你的程序(STS下,Maven可参考前面文章)**
你会看见如下的输出:
Sending message...
Received <Hello from RabbitMQ!>
您刚刚使用Spring和RabbitMQ开发了一个简单的发布和订阅应用程序。使用Spring和RabbitMQ可以做的比这里介绍的更多,但是这应该提供一个良好的开始。