文章目录
RabbitMq六种消息模型(SpringBoot版)
关于MQ的基本概念:学习MQ之前,你要先懂这些~_略懂Java的博客-CSDN博客
关于MQRabbitMq的安装:Linux安装RabbitMQ详细教程(下载安装及docker安装)_略懂Java的博客-CSDN博客
RabbitMQ简介
RabbitMQ是由erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中应用非常广泛。RabbitMQ官方地址:http://www.rabbitmq.com
RabbitMQ的工作原理
下图是RabbitMQ的基本结构:
组成部分说明:
-
Broker:消息队列服务进程,此进程包括两个部分:Exchange和Queue
-
Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过虑。
-
Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的
-
Producer:消息生产者,即生产方客户端,生产方客户端将消息发送
-
Consumer:消息消费者,即消费方客户端,接收MQ转发的消息。
生产者发送消息流程:
1、生产者和Broker建立TCP连接。
2、生产者和Broker建立通道。
3、生产者通过通道消息发送给Broker,由Exchange将消息进行转发。
4、Exchange将消息转发到指定的Queue(队列)
消费者接收消息流程:
1、消费者和Broker建立TCP连接
2、消费者和Broker建立通道
3、消费者监听指定的Queue(队列)
4、当有消息到达Queue时Broker默认将消息推送给消费者。
5、消费者接收到消息。
6、ack回复
Rabbit六种消息模型
RabbitMQ提供了6种消息模型,但是第6种其实是RPC,并不是MQ,因此不予学习。那么也就剩下5种。但是其实3、4、5这三种都属于订阅模型,只不过进行路由的方式不同。
基本消息模型(点对点)
在上图的模型中,有以下概念:
-
P:生产者,也就是要发送消息的程序
-
C:消费者:消息的接受者,会一直等待消息到来。
-
queue:消息队列,图中红色部分。可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。
新建一个maven工程,添加spring-boot-starter-web、spring-boot-starter-amqp依赖,另外添加一个spring-boot-starter-test依赖方便测试。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
在application.yml
中添加RabbitMQ的配置:
# 服务端口
server:
port: 8080
# 配置rabbitmq服务
spring:
rabbitmq:
username: rabbitmq
password: rabbitmq
virtual-host: /
host: #服务器公网地址
port: 5672
定义队列名称
public interface RabbitMqConstants {
String SMS_QUEUE = "sms_queue";
String EMAIL_QUEUE = "email_queue";
}
定义RabbitConfig配置类 初始化2个队列
@Configuration
public class RabbitMqConfig {
/**
* new Queue(QUEUE_EMAIL,true,false,false)
* durable="true" 持久化 rabbitmq重启的时候不需要创建新的队列
* auto-delete 表示消息队列没有在使用时将被自动删除 默认是false
* exclusive 表示该消息队列是否只在当前connection生效,默认是false
* @return
*/
@Bean
public Queue simpleSmsQueueInit() {
return new Queue(RabbitMqConstants.SMS_QUEUE, true);
}
/**
* 初始化邮箱队列
* @return
*/
@Bean
public Queue simpleEmailQueueInit() {
return new Queue(RabbitMqConstants.EMAIL_QUEUE, true);
}
}
定义生产者
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class Producer {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void sent(){
rabbitTemplate.convertAndSend(RabbitMqConstants.SMS_QUEUE,"Hello world");
}
}
我们使用@Test方法运行sent方法向SMS_QUEUE队列发送消息
在RabbitMq控制台我们可以看到初始化的2个队列并且SMS_QUEUE队列有一条待消费消息
点击队列名称在GetMessage下可以看到具体的消息内容
然后我们编写消费者类并监听SMS_QUEUE队列
我们添加@Commonent注解目的是让该类添加到Spring容器中,在项目启动的时候可以监听队列中的消息。
@Component
public class Consumer {
@RabbitListener(queues = RabbitMqConstants.SMS_QUEUE)
public void getMessage(String message){
System.out.println(message);
}
}
我们启动springBoot的启动类启动项目发现消息自动消费了
工作模式(work)
work queues与入门程序相比,多了一个消费端,两个消费端共同消费同一个队列中的消息,但是一个消息只能被一个消费者获取。
这个消息模型在Web应用程序中特别有用,可以处理短的HTTP请求窗口中无法处理复杂的任务。
生产者
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class WorkProducer {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void sent(){
for (int i = 1; i <=6 ; i++) {
rabbitTemplate.convertAndSend(RabbitMqConstants.EMAIL_QUEUE,"Hello world"+i);
}
}
}
消费者
@Component
public class WorkConsumer {
@RabbitListener(queues = RabbitMqConstants.EMAIL_QUEUE)
public void getMessage(String message){
System.out.println("消费者1接收到信息:"+message);
}
@RabbitListener(queues = RabbitMqConstants.EMAIL_QUEUE)
public void getMessageAgain(String message){
System.out.println("消费者2接收到信息:"+message);
}
}
订阅模型分类
-
一个生产者多个消费者
-
每个消费者都有一个自己的队列
-
生产者没有将消息直接发送给队列,而是发送给exchange(交换机、转发器)
-
每个队列都需要绑定到交换机上
-
生产者发送的消息,经过交换机到达队列,实现一个消息被多个消费者消费
Exchange类型
-
Fanout:广播,将消息交给所有绑定到交换机的队列
-
Direct:定向,把消息交给符合指定routing key 的队列
-
Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
广播模型(Fanout)
首先我们创建2个队列,分别是消息的队列和邮箱队列 创建一个fanout交换机,将这两个队列绑定到交换机上
public interface RabbitMqConstants {
String SMS_QUEUE = "sms_queue";
String EMAIL_QUEUE = "email_queue";
String FANOUT_EXCHANGE = "fanout_exchange";
}
@Configuration
public class RabbitMqConfig {
/**
* new Queue(QUEUE_EMAIL,true,false,false)
* durable="true" 持久化 rabbitmq重启的时候不需要创建新的队列
* auto-delete 表示消息队列没有在使用时将被自动删除 默认是false
* exclusive 表示该消息队列是否只在当前connection生效,默认是false
* @return
*/
@Bean
public Queue simpleSmsQueueInit() {
return new Queue(RabbitMqConstants.SMS_QUEUE, true);
}
/**
* 初始化邮箱队列
* @return
*/
@Bean
public Queue simpleEmailQueueInit() {
return new Queue(RabbitMqConstants.EMAIL_QUEUE, true);
}
/**
* 初始化fanout交换机
* @return
*/
@Bean
public FanoutExchange fanoutExchangeInit() {
return new FanoutExchange(RabbitMqConstants.FANOUT_EXCHANGE, true,false);
}
/**
* 短信队列绑定到交换机
* @param simpleSmsQueueInit
* @param fanoutExchange
* @return
*/
@Bean
public Binding bindingSmsQueue(Queue simpleSmsQueueInit, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(simpleSmsQueueInit).to(fanoutExchange);
}
/**
* 邮箱队列绑定到交换机
* @param simpleEmailQueueInit
* @param fanoutExchange
* @return
*/
@Bean
public Binding bindingEmailQueue(Queue simpleEmailQueueInit, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(simpleEmailQueueInit).to(fanoutExchange);
}
}
生产者
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class FanoutProducer {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void sent(){
rabbitTemplate.convertAndSend(RabbitMqConstants.FANOUT_EXCHANGE,"","我是交换机发出的消息");
}
}
消费者
@Component
public class FanoutConsumer {
@RabbitListener(queues = RabbitMqConstants.SMS_QUEUE)
public void getMessage(String message){
System.out.println("短信消费者接收到信息:"+message);
}
@RabbitListener(queues = RabbitMqConstants.EMAIL_QUEUE)
public void getMessageAgain(String message){
System.out.println("邮箱消费者接收到信息:"+message);
}
}
路由模式(direct)
P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。
X:Exchange(交换机),接收生产者的消息,然后把消息递交给 与routing key完全匹配的队列
初始化路由交换机
String SMS_QUEUE = "sms_queue";
String EMAIL_QUEUE = "email_queue";
String DIRECT_EXCHANGE = "direct_exchange";
@Configuration
public class RabbitMqConfig {
/**
* 初始化direct交换机
* @return
*/
@Bean
public DirectExchange directExchangeInit() {
return new DirectExchange(RabbitMqConstants.DIRECT_EXCHANGE, true,false);
}
/**
* 初始化短信队列
* @return
*/
@Bean
public Queue directSmsQueueInit() {
return new Queue(RabbitMqConstants.SMS_QUEUE, true);
}
/**
* 初始化邮箱队列
* @return
*/
@Bean
public Queue directEmailQueueInit() {
return new Queue(RabbitMqConstants.EMAIL_QUEUE, true);
}
/**
* 短信队列绑定到交换机
* @param directSmsQueueInit
* @param directExchange
* @return
*/
@Bean
public Binding directBindingSmsQueue(Queue directSmsQueueInit, DirectExchange directExchange) {
return BindingBuilder.bind(directSmsQueueInit).to(directExchange).with("sms");
}
/**
* 邮箱队列绑定到交换机
* @param directEmailQueueInit
* @param directExchange
* @return
*/
@Bean
public Binding directBindingEmailQueue(Queue directEmailQueueInit, DirectExchange directExchange) {
return BindingBuilder.bind(directEmailQueueInit).to(directExchange).with("email");
}
}
生产者
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class DirectProducer {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void sent(){
rabbitTemplate.convertAndSend(RabbitMqConstants.DIRECT_EXCHANGE,"email","我是交换机发出的消息");
}
}
消费者
@Component
public class DirectConsumer {
@RabbitListener(queues = RabbitMqConstants.SMS_QUEUE)
public void getMessage(String message){
System.out.println("短信消费者接收到信息:"+message);
}
@RabbitListener(queues = RabbitMqConstants.EMAIL_QUEUE)
public void getMessageAgain(String message){
System.out.println("邮箱消费者接收到信息:"+message);
}
}
主题模式(Topic)
每个消费者监听自己的队列,并且设置带统配符的routingkey,生产者将消息发给broker,由交换机根据routingkey来转发消息到指定的队列。
Routingkey一般都是有一个或者多个单词组成,多个单词之间以“.”分割,例如:inform.sms
通配符规则:
#
:匹配一个或多个词
*
:匹配不多不少恰好1个词
举例:
sms.#
:能够匹配sms.irs.corporate
或者 sms.irs
email.*
:只能匹配email.irs
public interface RabbitMqConstants {
String TOPIC_EXCHANGE = "topic_exchange";
String SMS_TOPIC_QUEUE = "sms_topic_queue";
String EMAIL_TOPIC_QUEUE = "email_topic_queue";
}
@Configuration
public class RabbitMqConfig {
/**
* 初始化topic交换机
* @return
*/
@Bean
public TopicExchange topicExchangeInit() {
return new TopicExchange(RabbitMqConstants.TOPIC_EXCHANGE, true,false);
}
/**
* 初始化短信队列
* @return
*/
@Bean
public Queue topicSmsQueueInit() {
return new Queue(RabbitMqConstants.SMS_TOPIC_QUEUE, true);
}
/**
* 初始化邮箱队列
* @return
*/
@Bean
public Queue topicEmailQueueInit() {
return new Queue(RabbitMqConstants.EMAIL_TOPIC_QUEUE, true);
}
/**
* 短信队列绑定到交换机
* @param topicSmsQueueInit
* @param topicExchangeInit
* @return
*/
@Bean
public Binding topicBindingSmsQueue(Queue topicSmsQueueInit, TopicExchange topicExchangeInit) {
return BindingBuilder.bind(topicSmsQueueInit).to(topicExchangeInit).with("sms.#");
}
/**
* 邮箱队列绑定到交换机
* @param topicEmailQueueInit
* @param topicExchangeInit
* @return
*/
@Bean
public Binding topicBindingEmailQueue(Queue topicEmailQueueInit, TopicExchange topicExchangeInit) {
return BindingBuilder.bind(topicEmailQueueInit).to(topicExchangeInit).with("email.*");
}
}
生产者
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class TopicProducer {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void sent(){
rabbitTemplate.convertAndSend(RabbitMqConstants.TOPIC_EXCHANGE,"sms.x.x","我是主题模式发出的消息1");
rabbitTemplate.convertAndSend(RabbitMqConstants.TOPIC_EXCHANGE,"email.x","我是主题模式发出的消息2");
}
}
消费者
@Component
public class TopicConsumer {
@RabbitListener(queues = RabbitMqConstants.SMS_TOPIC_QUEUE)
public void getMessage(String message){
System.out.println("主题模式短信消费者接收到信息:"+message);
}
@RabbitListener(queues = RabbitMqConstants.EMAIL_TOPIC_QUEUE)
public void getMessageAgain(String message){
System.out.println("主题模式邮箱消费者接收到信息:"+message);
}
}