消息在RabbitMQ队列传输过程中,根据不同的传输方式,以及所使用的队列种类不同,一共划分了5个消息传输模式。
RabbitMQ所实现的消息投递方式来来划分,可以将消息发送模式分为两大类,
点对点模式
和发布订阅模式
;根据队列匹配规则不同发送模式分为五大类普通队列模式、工作队列模式、发布订阅模式、直接模式、主体模式
。
将消息直接发送给消费者
允许多个队列绑定到一个交换机上,在生产者发送消息给交换机时,需要携带一个 Key ,而这个 key 一般称为 routing key 或 binding key ,直接模式也被称为路由模式
//生产者
//Routing Key 路由key名称
channel.basicPublish(EXCHANGE_NAME, "Routing Key",..)
//消费者
//使用channel方式绑定名为 Routin Key 的直接交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME ,"Routing Key")
生产者发布消息,消费者通过订阅的方式来消费消息(群发)
消息会发送到每一个绑定的队列上
//生产者
//交换机类型指定为 fanout ,
channel.exchangeDeclare(EXCHANGE_NAME,"fanout")//fanout 交换机类型
//消费者
//为交换机绑定一个队列,如果没绑定队列到交换机上,则消息会丢失
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"")
//接收消息队列发送到消息并消费
channel.basicConsume(QUEUE_NAME,false,consumer)
不使用任何交换机,由生产者,队列,消费者组合完成的发送接收
//生产者
//绑定消息队列,指定消息队列的名称
channel.queueDeclare(QUEUE_NAME,false,false,false,null)
//消费者
//basicConsume 来接收从队列中的消息
channel.basicConsume(QUEUE_NAME,true,consumer)
不使用任何交换机,由生产者、队列、消费者组合完成消息的发送和接
收,工作队列支持存在多个消费者
(普通队列模式之支持一个消费者)
//生产者
channel.basicPublish(QUEUE_NAME,null,message.getBytes());
Thread.sleep(1000);
//消费者
//获取队列中的消息
Delivery deliver = consumer.nextDelivery();
Thread.sleep(1000);
交换机与消息队列所绑定的key值可以像匹配通配符的方式,来匹配消息队列。
//生产者
//设置交换机 模式
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
//消息绑定到交换机
channel.basicPublish(EXCHANGE_NAME,"key.123",..);
//消费者
//将队列绑定到交换机上,设置key 的匹配策略
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"key.*");
主要作用: 将生产者生产出来的消息,传送到对应频道中,交换机是一个消息传递媒介,英文 exchange 。交换机在RabbitMQ中起着承上启下的作用。
常用的的三种
名称 | 类型 |
---|---|
直接交换机 | Direct |
扇形交换机 | Fanout |
主题交换机 | Topic |
将消息根据特定的匹配规则发送到对应的消息队列的交换机,如果匹配规则相同,则一条消息可以被发送到多个对应的队列上,匹配规则时通过routing——key 来进行匹配
String queueName = "test_direct_x";
channel.exchangeDeclare("direct_exchange","direct");
channel.queueDeclare(queueName,true,fasle,fasle,null);
第1行,声明一个名为 test_direct_x 队列名称,对于直通交换机而言,这个名称就是 routing_key
第2行,exchangeDeclare声明一个交换机,交换机名: direct_exchange
,交换机类型direct
第3行,使用queueDeclare来声明一个队列, queueName
是我们声明的test_direct_x队列
消息在经过 direct_exchange 交换机之后,会根据名为 test_direct_x的routing_key与相应的消息队列进行匹配,如果消息队列1,队列2,队列3都与该routing_key相匹配那么我们的消息都会全部流转到这三个消息队列中
通过广播的形式,将消息传递到消息队列中,
扇形交换机不需要绑定routing_key
,会将消息传递到所有与该交换机绑定的消息队列中去
String queueName = "test_fanout_x";
channel.exchangeDeclare("fanout_exchange","fanout");
channel.queueBind(queueName,"fanout_exchange","");
第1行,声明了一个名为 test_fanout_x 队列。
第2行, exchangeDeclare 声明一个交换机,
第3行, queueBind 将交换机与消息队列进行绑定,
消息进入 fanout_exchange 交换机之后,会首先检查有没已经与该交换机进行绑定的消息队列,没有绑定队列,消息自动失效,且抛出异常。
上图中,消息会被广播的。队列1-3中
routing_key 模糊匹配
String queueName = "test.topic.x";
channel.exchangeDeclare("topic_exchange","topic");
channel.queueBind(queueName,"fanout_exchange","test.#");
消息静如 topic_exchange 中按照 rrouting_key 的规则去匹配队列,会去匹配每一个队列,符号规则就发送消息。
常用APi | 作用 |
---|---|
declareExchange() | 声明交换机 |
declareQueue() | 声明队列 |
declareBinding() | 交换机与队列进行绑定 |
purgeQueue() | 清空指定的队列中所有消息的方法 |
//交换机名 ,是否持久化,是否自动删除
new DirectExchange("test,name",false,false);
//队列名, 是否持久化
new Queue("test.name.queue",false);
@Autowired
private RabbitAdmin rabbitAdmin;
rabbitAdmin.declareExchange(new DirectExchange("test.direct", false, false));
rabbitAdmin.declareQueue(new Queue("test.direct.queue", false));
//test.name.queue 声明队列
//Binding.DestinationType.QUEUE 绑定类型
//test.name 声明交换机
// direct 交换机类型
// map 是 arguments属性
rabbitAdmin.declareBinding(new Binding("test.name.queue", Binding.DestinationType.QUEUE,
"test.name", "direct", new HashMap<>()));
api | 说明 |
---|---|
MessageProperties | 对消息properties参数描述 |
Message | 对消息体进行描述 |
send() | 将原始消息发送到RabbitMQ server中 |
convertAndSend() | 用于原始消息进行转换,并且将转换后的消息发送到,RabbitMQ servier中 |
addListener() | 当前消息模版设置消息监听类型 |
@Autowired
private RabbitTemplate rabbitTemplate;
MessageProperties messageProperties = new MessageProperties();
//获取 headers 参数
messageProperties.getHeaders().put("test1", "test1");
messageProperties.getHeaders().put("test2", "test2");
//初始化一个message 设置了 消息, 参数
Message message = new Message("Hello RabbitTemplate".getBytes(), messageProperties);
//消息转换
//test_direct_001 交换机名
//test.123 路由 routingKey
//message 发送消息
// 发送消息成功后到监听器
rabbitTemplate.convertAndSend("test_direct_001", "test.123", message, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().getHeaders().put("test1", "test111");
message.getMessageProperties().getHeaders().put("test2", "test222");
return message;
}
});
<-- rabbitMQ 依赖 -->
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<-- 引入rabbitTemplate -->
<dependency>
<groupId>com.rabbitmqgroupId>
<artifactId>amqp-clientartifactId>
<version>3.6.5version>
dependency>
spring:
rabbitmq:
host: 119.91.00.00
port: 5672
username: admin
password: admin
virtual-host: my
package com.imooc.api.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author LFweixiao
* @create 2022-04-06 8:11 下午
*/
@Configuration
public class RabbitMQConfig {
//定义交换机名称
public static final String EXCHANGE_ARTICLE = "exchange_article";
//定义队列名称
public static final String QUEUE_DOWNLOAD_HTML = "queue_download_html";
//创建交换机,放入springboot 容器
@Bean(EXCHANGE_ARTICLE)
public Exchange exchange() {
return ExchangeBuilder //构建交换机
.topicExchange(EXCHANGE_ARTICLE) //使用 topic 类型交换机
.durable(true) //设置持久化,重启MQ后依然存在
.build();
}
//创建队列
@Bean(QUEUE_DOWNLOAD_HTML)
public Queue queue(){
return new Queue(QUEUE_DOWNLOAD_HTML);
}
//队列绑定交换机
@Bean
public Binding binding(
@Qualifier(QUEUE_DOWNLOAD_HTML) Queue queue,
@Qualifier(EXCHANGE_ARTICLE) Exchange exchange
) {
return BindingBuilder //定义绑定关系
.bind(queue) //绑定队列
.to(exchange) //绑定交换机
.with("article.*") //定义路由规则(requestMapping 映射)
.noargs(); //执行绑定
}
}
public class{
@GetMapping("hello1")
public Object hello1() {
// 往 EXCHANGE_ARTICLE 发送一条消息,并且指定相应的路由规则
rabbitTemplate.convertAndSend(
RabbitMQConfig.EXCHANGE_ARTICLE //绑定 交换机
,"article.download" // 路由 routingKey
,"发送消息" //消息体
);
return "ok";
}
@Component
public class RabbitMQConsumer {
/**
* 监听MQ 消息队列
* @param payload 消息内容
* @param message 消息对象
*/
@RabbitListener(queues = {RabbitMQConfig.QUEUE_DOWNLOAD_HTML})
public void watchQueue(String payload, Message message) {
//System.out.println(payload);
//获取消息 路径
String routingKey = message.getMessageProperties().getReceivedRoutingKey();
if (routingKey.equalsIgnoreCase("article.download")) {
//业务处理
}else if(routingKey.equalsIgnoreCase("article.download.do")){
//业务处理
}else {
//业务处理
}
}
}
需要安装x-delayed-message
@Configuration
public class RabbitMQDelayConfig {
// 定义交换机的名称
public static final String EXCHANGE_DELAY = "exchange_delay";
// 定义队列的名称
public static final String QUEUE_DELAY = "queue_delay";
//创建 延迟队列交换机
@Bean(EXCHANGE_DELAY)
public Exchange exchange(){
return ExchangeBuilder
.topicExchange(EXCHANGE_DELAY) //构建 topic 类型
.durable(true) //持久化
.delayed() // 设置为延迟队列
.build(); //构建
}
// 创建队列
@Bean(QUEUE_DELAY)
public Queue queue() {
return new Queue(QUEUE_DELAY);
}
// 队列绑定交换机
@Bean
public Binding delayBinding(
@Qualifier(QUEUE_DELAY) Queue queue,
@Qualifier(EXCHANGE_DELAY) Exchange exchange) {
return BindingBuilder // 定义绑定关系
.bind(queue) // 绑定队列
.to(exchange) // 到交换机
.with("delay.*") // 定义路由规则(requestMapping映射)
.noargs(); // 执行绑定
}
}
注意: 此处idea 自动导入的包是错误的,正确的包是
import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessagePostProcessor;
@GetMapping("mq02")
public Object mq02() {
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
// 设置持久
message.getMessageProperties()
.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
// 设置延迟的时间,单位毫秒
message.getMessageProperties().setDelay(5000);
return message;
}
};
rabbitTemplate.convertAndSend(
RabbitMQDelayConfig.EXCHANGE_DELAY //延迟队列 绑定交换机
,"delay.test" //路由 routingKey
,"延迟队列消息"
,messagePostProcessor
);
return "ok";
}