第五节 SpringBoot简单集成RabbitMQ
安装RabbitMQ
参考链接:windows下 安装 rabbitMQ 及操作常用命令
RabbitMQ入门(一)——RabbitMQ的安装以及使用(Windows环境下)
一、导入依赖
首先创建一个基本的SpringBoot项目后,导入RabbitMQ的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
二、编写配置文件
使用默认的端口,并且使用guest帐号进行登录。
spring.application.name=spirng-boot-rabbitmq
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
三、创建两个队列
我们来创建俩个队列,队列A和队列B。
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMqConfig {
public static final String QUEUE_NAME_A = "queueA";
public static final String QUEUE_NAME_B = "queueB";
@Bean
public Queue queue1() {
return new Queue(QUEUE_NAME_A);
}
@Bean
public Queue queue2() {
return new Queue(QUEUE_NAME_B);
}
}
四、创建接收者
接收者相当于就是消息的消费者,它从队列里面获取消息。
接收者对象使用@RabbitListener注解来说明它接收的是哪个队列的消息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "queueA")
public class Receiver1 {
private Logger logger = LoggerFactory.getLogger("Receiver1");
@RabbitHandler
public void receiver(String msg) {
System.out.println("接收者1号获取到数据" + msg);
}
}
五、向单个接收者发送消息
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
private AmqpTemplate amqpTemplate;
@Test
public void send() {
String message = "Hello world";
String receiver = "queueA";
amqpTemplate.convertAndSend(receiver, message);
}
}
运行SpringBoot的main函数后,我们向队列queueA发送一个消息"hellow world"。然后监听这个队列的接收者是Receiver1类。所以执行 Receiver1类的使用@RabbitHandler注解的方法进行处理。成功在控制台上打印出数据。
六、向多个接收者发送消息
再创建一个接收队列queueA的接收者:接收者2号。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "queueA")
public class Receiver2 {
private Logger logger = LoggerFactory.getLogger("Receiver2");
@RabbitHandler
public void receiver(String msg) {
System.out.println("接收者2号获取到数据" + msg);
}
}
再次测试发送消息,这次循环发送消息。消息向队列queueA发送,监听queueA队列的两个接收者:接收者1号和接收者2号,会自动负载均衡。并且每个消息只会被接收一次。
@Test
public void sendManyTimes() {
String message = "Hello world";
String receiver = "queueA";
for (int i = 0; i < 10; i++) {
amqpTemplate.convertAndSend(receiver, message + i);
}
}
七、向多个队列发送消息
修改接收者2号,让它监听队列B。修改后的代码为
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "queueB")
public class Receiver2 {
private Logger logger = LoggerFactory.getLogger("Receiver2");
@RabbitHandler
public void receiver(String msg) {
System.out.println("接收者2号获取到数据" + msg);
}
}
然后我们再来测试一下,分别向队列A和队列B发送5条消息。监听队列A的接收者1号应该会收到5条消息,监听队列B的接收者2号也会接收到5条消息。
根据结果来看,这次每条消息都分别发送给了接收者1号和接收者2号。
@Test
public void sendManyTimes2() {
String message = "Hello world";
String receiverA = "queueA";
String receiverB = "queueB";
for (int i = 0; i < 5; i++) {
amqpTemplate.convertAndSend(receiverA, message + i);
amqpTemplate.convertAndSend(receiverB, message + i);
}
}
八、使用Topic Exchange广播
Topic Exchange是RabbitMQ中最灵活的一种方式,任何发送到Topic Exchange的消息都会被转发到所有关心RouteKey中指定话题的Queue上。使用的是一种正则匹配规则。生产者会发送一个带路由键的消息。Exchange会将消息转发到所有关注主题能与RouteKey模糊匹配的队列。
创建一个新的配置类,代码如下。创建了两个队列和一个路由器。
路由器如果接收到的路由键是topic.message,那么就把消息发送到queueMessage队列。监听queueMessage队列的接收者就能收到消息了。
路由器如果接收到的路由键是topic开头的,那么就把消息发送到queueMessages队列。监听queueMessages队列的接收者就能收到消息了。
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author jay.zhou
* @date 2019/4/8
* @time 15:16
*/
@Configuration
public class RabbitMqTopicConfig {
/**
* message队列名
*/
final String message = "topic.message";
/**
* messages队列名
*/
final String messages = "topic.messages";
/**
* 初始化一个叫topic.message的队列
*/
@Bean
public Queue queueMessage() {
return new Queue(message);
}
/**
* 初始化一个叫topic.messages的队列
*/
@Bean
public Queue queueMessages() {
return new Queue(messages);
}
/**
* 创建一个叫exchange的路由器
* 路由器负责分发消息到指定的队列
*/
@Bean
TopicExchange exchange() {
return new TopicExchange("exchange");
}
/**
* 约定大于配置
* 这个方法的参数queueMessage就是本类中queueMessage()方法的返回值
* 这个方法的参数exchange就是本类中exchange()方法的返回值
* 如果发送者投递的路由键是:topic.message,那么就把数据发送到queueMessage队列中
*/
@Bean
Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message");
}
/**
* 约定大于配置
* 这个方法的参数queueMessage就是本类中queueMessage()方法的返回值
* 这个方法的参数exchange就是本类中exchange()方法的返回值
* 如果发送者投递的路由键是:topic.#,即路由键使用topic开头的
* 那么就把数据发送到queueMessages队列中
*/
@Bean
Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) {
//这里的#表示零个或多个词。
return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#");
}
}
接着,我们创建接收者。
接收者1号监听topic.message的队列。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "topic.message")
public class TopicReceiver1 {
private Logger logger = LoggerFactory.getLogger("TopicReceiver1");
@RabbitHandler
public void process(String msg) {
System.out.println("接收者1号接收到信息:" + msg);
}
}
接收者2号监听topic.messages的队列。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "topic.messages")
public class TopicReceiver2 {
private Logger logger = LoggerFactory.getLogger("TopicReceiver2");
@RabbitHandler
public void process(String msg) {
System.out.println("接收者2号接收到信息:" + msg);
}
}
最后,创建测试方法。
数据message交给交换器exchange,交换器根据路由键route_key,去配置类中匹配符合要求的队列。
监听符合要求队列queue的接收者将会收到消息。
@Test
public void test2() {
String message = "hello world";
System.out.println("发送者说 : " + message);
String exchangeName = "exchange";
String route_key= "topic.message";
//数据message交给交换器exchange,交换器将其投递到队列中
//监听这个队列的接收者将会收到消息
this.amqpTemplate.convertAndSend(exchangeName, route_key, message);
}
我们发送的路由键的名字是topic.message,所以上图中的红色框框的配置。第一个红框表示:消息的路由键如果是"topic.message"的消息,交换机将会把消息投递到queueMessage队列中。第二个红框表示:消息的路由键如果已topic开头,那么路由器将会把这个消息投递到queueMessages队列。所以监听这些队列的接收者将会收到消息。接收者1号监听的是topic.message队列。接收者2号监听的是topic.messages队列。所以,1号和2号都收到了消息。
最后再测一下,如果把消息的路由键修改为topic开头的数据,测试接收者1号能不能收到数据。预期是不能收到。
路由键以topic开头,后面的可以是任何字符串。发送出去的时候,消息将会被路由器投递到queueMessages队列。监听queueMessages队列的接收者是2号。所以运行结果是2号收到消息。
@Test
public void test3() {
String message = "hello world";
System.out.println("发送者说 : " + message);
String exchangeName = "exchange";
String route_key = "topic.asdkjqwidjasldkj";
//数据message交给交换器exchange,交换器将其投递到队列中
//监听这个队列的接收者将会收到消息
this.amqpTemplate.convertAndSend(exchangeName, route_key, message);
}
九、Fanout Exchange(订阅模式)
topic类型的广播形式,路由器根据消息的路由键,最后将消息投递到指定的队列中。
发送到exchange的所有消息会被转发到与exchange绑定的所有queue,不需要处理路由
fanout类型的广播形式,
首先创建两个队列,名字是queueA和queueB。然后再创建一个Fanout路由器。这种类型的路由器,会把收到的消息,直接投递到绑定它的所有的队列中,不用路由键,直接不BB,投递就完事了。接收者可从队列中获取消息。
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMqFanoutConfig {
/**
* 初始化一个叫queueA的队列
*/
@Bean
public Queue queueA() {
return new Queue("queueA");
}
/**
* 初始化一个叫queueB的队列
*/
@Bean
public Queue queueB() {
return new Queue("queueB");
}
/**
* 创建一个叫fanoutExchange的路由器
* 路由器负责分发消息到指定的队列
*/
@Bean
FanoutExchange exchange() {
return new FanoutExchange("fanoutExchange");
}
/**
* 直接把队列绑定到交换机身上,不用路由键
* 也就是说,交换机把消息直接投递到绑定它的队列中
* 不需要路由键
*/
@Bean
Binding bindingExchangeMessage(Queue queueA, FanoutExchange exchange) {
return BindingBuilder.bind(queueA).to(exchange);
}
@Bean
Binding bindingExchangeMessages(Queue queueB, FanoutExchange exchange) {
return BindingBuilder.bind(queueB).to(exchange);
}
}
我们再来创建两个接收者。接收者1号接收来自队列A的消息,接收者2号接收来自队列B的消息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "queueA")
public class FanoutReceiver1 {
private Logger logger = LoggerFactory.getLogger("FanoutReceiver1");
@RabbitHandler
public void process(String msg) {
System.out.println("接收者1号接收到信息:" + msg);
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "queueB")
public class FanoutReceiver2 {
private Logger logger = LoggerFactory.getLogger("FanoutReceiver2");
@RabbitHandler
public void process(String msg) {
System.out.println("接收者2号接收到信息:" + msg);
}
}
最后,编写测试文件。
注意,测试的时候删除掉RabbitMqTopicConfig类,我测试的时候发现好像不能同时创建Topic类型和Fanout类型的交换机。为了不影响测试,得删除掉Topic类型的交换机。
因为消息发送给了Fanout交换机,所以交换机会把消息发送给绑定它的队列,分别是队列A和队列B。接收者1号会从队列A中获取消息,接收者2号会从队列B中获取消息。最后运行的结果是:接收者1号和2号都会接收到消息。
@Test
public void fanoutMessage() {
String message = "hello world";
//路由键在Fanout广播形式中无效
String route_key = "";
System.out.println("发送者说 : " + message);
String exchangeName = "fanoutExchange";
//数据message交给交换器exchange,交换器将其投递到队列中
//监听这个队列的接收者将会收到消息
this.amqpTemplate.convertAndSend(exchangeName, route_key, message);
}
源代码下载
源代码地址:https://github.com/hairdryre/Study_RabbitMQ(这一篇的代码可以直接CSDN上复制)
下一篇:第六节 SpringBoot集成RabbitMQ综合运用(SSM框架集成RabbitMQ)
阅读更多:从头开始学RabbimtMQ目录贴