RabbitMQ的工作模式
(1)工作队列模式
![](https://img-blog.csdnimg.cn/img_convert/f54b3884b4b97a20621b82967a395c74.png)
(2)发布订阅模式
(多了一个exchange交换机)
![](https://img-blog.csdnimg.cn/img_convert/318adae244dc48d30eee093137f1e322.png)
(3)路由模式
(需要指定routing key路由)
![](https://img-blog.csdnimg.cn/img_convert/40ba8e54815453f88f4c593ffdc41ffc.png)
(4)通配符模式
通配符规则:# 匹配一个或多个词,* 匹配不多不少恰好1个词,例如:item.# 能够匹配 item.insert.abc 或者 item.insert,item.* 只能匹配 item.insert
![](https://img-blog.csdnimg.cn/img_convert/31d8d7d13d1be0b541464574a2a380f1.png)
2.SpringBoot和rabbitMQ的整合
producer模块中,yml配置文件
# 配置RabbitMQ的基本信息 ip 端口 username password..
spring:
rabbitmq:
host: xxxxx # ip
port: 5672
username: bq123
password: bq123
virtual-host: /baiqi
声明一个配置类,把交换机和队列进行绑定
@Configuration
public class RabbitMqConfig {
//定义交换机的名字
public static final String EXCHANGE_NAME = "boot_topic_exchange";
//定义队列的名字
public static final String QUEUE_NAME = "boot_queue";
//1、声明交换机
@Bean("bootExchange")
public Exchange bootExchange(){
return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
}
//2、声明队列
@Bean("bootQueue")
public Queue bootQueue(){
return QueueBuilder.durable(QUEUE_NAME).build();
}
//3、队列与交换机进行绑定
@Bean
public Binding bindQueueExchange(@Qualifier("bootQueue") Queue queue, @Qualifier("bootExchange") Exchange exchange){
//这个 boot.#是 声明一个路由的key (topic类型的交换机)
return BindingBuilder.bind(queue).to(exchange).with("boot.#").noargs();
}
}
注入rabbitTemplate @Autowired,完成数据的绑定
@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {
//注入 RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void send(){
//交换机的名字
rabbitTemplate.convertAndSend("boot_topic_exchange","boot.haha","boot mq...");
}
}
消费者监听器 @RabbitListener,完成对数据的监听
@Component
public class RabbitMQListener {
//定义方法进行信息的监听 RabbitListener中的参数用于表示监听的是哪一个队列
@RabbitListener(queues = "boot_queue")
public void ListenerQueue(Message message){
System.out.println("message:"+message.getBody());
}
}
3.RabbitMQ的高级特性
(1)Producer保证消息的可靠性传递 (confirm,return) 与Consumer ACK
在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。
confirm 确认模式
return 退回模式
rabbitmq 整个消息投递的路径为:
producer--->rabbitmq broker--->exchange--->queue--->consumer
消息从 producer 到 exchange 则会返回一个 confirmCallback 。
消息从 exchange-->queue 投递失败则会返回一个 returnCallback 。
我们将利用这两个 callback 控制消息的可靠性投递
<!--消息可靠性投递(生产端)--> xml
<rabbit:queue id="test_queue_confirm" name="test_queue_confirm"></rabbit:queue>
<rabbit:direct-exchange name="test_exchange_confirm">
<rabbit:bindings>
<rabbit:binding queue="test_queue_confirm" key="confirm"></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
测试1:确认模式
@Test
public void testConfirm() {
//定义回调
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
*
* @param correlationData 相关配置信息
* @param ack exchange交换机 是否成功收到了消息。true 成功,false代表失败
* @param cause 失败原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("confirm方法被执行了....");
//ack 为 true表示 消息已经到达交换机
if (ack) {
//接收成功
System.out.println("接收成功消息" + cause);
} else {
//接收失败
System.out.println("接收失败消息" + cause);
//做一些处理,让消息再次发送。
}
}
});
//进行消息发送
rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message Confirm...");
//进行睡眠操作
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
}
测试2:退回模式
@Test
public void testReturn() {
//设置交换机处理失败消息的模式 为true的时候,消息达到不了 队列时,会将消息重新返回给生产者
rabbitTemplate.setMandatory(true);
//定义回调
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
/**
*
* @param message 消息对象
* @param replyCode 错误码
* @param replyText 错误信息
* @param exchange 交换机
* @param routingKey 路由键
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("return 执行了....");
System.out.println("message:"+message);
System.out.println("replyCode:"+replyCode);
System.out.println("replyText:"+replyText);
System.out.println("exchange:"+exchange);
System.out.println("routingKey:"+routingKey);
//处理
}
});
//进行消息发送
rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message return...");
//进行睡眠操作
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
}
Consumer --- ACK (implements ChannelAwareMessageListener)
![](https://img-blog.csdnimg.cn/img_convert/71d89f32f8ea5179c426f6da2067189f.png)
<!--定义监听器容器
acknowledge="manual":手动签收
prefetch="1":每次抓取多少条消息
-->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" prefetch="2" >
<rabbit:listener ref="ackListener" queue-names="test_queue_confirm"></rabbit:listener>
</rabbit:listener-container>
注意 channel.basicAck(deliveryTag, true);
channel.basicNack(deliveryTag, true, true);
@Component
public class AckListener implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
//1、获取消息的id
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
//2、获取消息
System.out.println("message:"+new String(message.getBody()));
//3、进行业务处理
System.out.println("=====进行业务处理====");
//模拟出现异常
int i = 5/0;
//4、进行消息签收
channel.basicAck(deliveryTag, true);
} catch (Exception e) {
//拒绝签收
/*
第三个参数:requeue:重回队列。如果设置为true,则消息重新回到queue,broker会重新发送该消息给消费端
*/
channel.basicNack(deliveryTag, true, true);
}
}
}
(2)消费端限流 prefetch="2"
![](https://img-blog.csdnimg.cn/img_convert/11bf26c8f0eaf35e7e5c69eb61d9d333.png)
<!--定义监听器容器
acknowledge="manual":手动签收
prefetch="1":每次抓取多少条消息
-->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" prefetch="2" >
<rabbit:listener ref="ackListener" queue-names="test_queue_confirm"></rabbit:listener>
</rabbit:listener-container>
(3)TTL (time to live 存活时间)
key="x-message-ttl" 这个是固定的
![](https://img-blog.csdnimg.cn/img_convert/e833b0ed103381776614a8715908d292.png)
![](https://img-blog.csdnimg.cn/img_convert/5a3fa5939592bf1164ed2a1890f03d6d.png)
<rabbit:queue name="test_queue_ttl" id="test_queue_ttl">
<!--设置queue的参数-->
<rabbit:queue-arguments>
<!--x-message-ttl指队列的过期时间-->
<entry key="x-message-ttl" value="100000" value-type="java.lang.Integer"></entry>
</rabbit:queue-arguments>
</rabbit:queue>
<rabbit:topic-exchange name="test_exchange_ttl" >
<rabbit:bindings>
<rabbit:binding pattern="ttl.#" queue="test_queue_ttl"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
(4)死信队列 (Dead Letter Exchange 死信交换机)
![](https://img-blog.csdnimg.cn/img_convert/8f48ab709dbc152f38db3aacc5be0524.png)
死信队列:
1. 声明正常的队列(test_queue_dlx)和交换机(test_exchange_dlx)
2. 声明死信队列(queue_dlx)和死信交换机(exchange_dlx)
3. 正常队列绑定死信交换机
设置两个参数:
* x-dead-letter-exchange:死信交换机名称
* x-dead-letter-routing-key:发送给死信交换机的routingkey
<!--
1. 声明正常的队列(test_queue_dlx)和交换机(test_exchange_dlx)
-->
<rabbit:queue name="test_queue_dlx" id="test_queue_dlx">
<! -- 正常队列绑定死信交换机-->
<rabbit:queue-arguments>
<! --3.1 x-dead-letter-exchange:死信交换机名称-->
<entry key="x-dead-letter-exchange" value="exchange_dlx" />
<! --3.2 x-dead-letter-routing-key:发送给死信交换机的routingkey-->
<entry key="x-dead-letter-routing-key" value="dlx.hehe" />
<! -- 4.1 设置队列的过期时间 ttl -->
<entry key="x-message-ttl" value="10000" value-type="java.lang.Integer" />
<! -- 4.2 设置队列的长度限制 max-length -->
<entry key="x-max-length" value="10" value-type="java.lang.Integer" />
</rabbit:queue-arguments>
</rabbit:queue>
<rabbit:topic-exchange name="test_exchange_dlx">
<rabbit:bindings>
<rabbit:binding pattern="test.dlx.#" queue="test_queue_dlx"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
<!--2. 定义死信交换机(order_exchange_dlx)和队列(order_queue_dlx)-->
<rabbit:queue id="order_queue_dlx" name="order_queue_dlx"></rabbit:queue>
<rabbit:topic-exchange name="order_exchange_dlx">
<rabbit:bindings>
<rabbit:binding pattern="dlx.order.#" queue="order_queue_dlx"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
消费者的监听器 监听死信交换机的下一个队列:order_queue_dlx
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" prefetch="2" >
<!--延迟队列效果实现: 一定要监听的是 死信队列!!!-->
<rabbit:listener ref="orderListener" queue-names="order_queue_dlx"></rabbit:listener>
</rabbit:listener-container>
![](https://img-blog.csdnimg.cn/img_convert/c555aed405be17e7c6568a4390db523a.png)
(5)延时队列 (很可惜,在RabbitMQ中并未提供延迟队列功能)
但是可以使用:TTL+死信队列 组合实现延迟队列的效果
![](https://img-blog.csdnimg.cn/img_convert/3a0228bb474d79649ad3b02f9d308667.png)