一、前言
本文演示的延迟队列是基于SpringBoot整合RabbitMQ来实现的,关于SpringBoot整合RabbitMQ可参考该链接:https://blog.csdn.net/pkxwyf/article/details/105158608
二、延迟队列概述和原理
2.1、什么是延迟队列
所谓延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。
2.1、延迟队列应用场景
- 用户下单后,30分钟未支付,取消订单恢复库存。
2.3、延迟队列实现原理
很可惜的是在RabbitMQ中并未提供延迟队列功能,但是可以使用:TTL+死信队列 组合实现延迟队列的效果
三、延迟队列代码演示
3.1、消息生产方代码实现
1、编写配置类:定义交换机和队列信息
@Configuration
public class RabbitMQConfig {
// 普通交换机名称
public static final String EXCHANGE_NAME = "order_topic_exchange";
// 普通队列名称
public static final String QUEUE_NAME = "order_queue";
// 死信交换机名称
public static final String DEAD_EXCHANGE_NAME = "dead_order_topic_exchange";
// 死信队列名称
public static final String DEAD_QUEUE_NAME = "dead_order_queue";
// 1. 定义普通交换机
@Bean("orderExchange")
public Exchange createExchange(){
return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
}
// 2. 定义普通队列
@Bean("orderQueue")
public Queue createQueue(){
// 创建map集合:封装队列参数
Map<String,Object> map = new HashMap<>();
// 设置死信交换机名称
map.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);
// 设置发送给死信交换机的routingKey
map.put("x-dead-letter-routing-key", "dead.order.news");
// 设置队列过期时间:单位毫秒
map.put("x-message-ttl", 10000);
return QueueBuilder.durable(QUEUE_NAME).withArguments(map).build();
}
// 3. 定义死信交换机
@Bean("deadOrderExchange")
public Exchange createDeadExchange(){
return ExchangeBuilder.topicExchange(DEAD_EXCHANGE_NAME).durable(true).build();
}
// 4. 定义死信队列
@Bean("deadOrderQueue")
public Queue createDeadQueue(){
// 创建map集合:封装队列参数
return QueueBuilder.durable(DEAD_QUEUE_NAME).build();
}
// 5. 队列与交换机绑定关系
@Bean
public Binding bindExchangeAndQueue(@Qualifier("orderQueue") Queue queue,
@Qualifier("orderExchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("order.#").noargs();
}
// 6. 死信队列与死信交换机绑定关系
@Bean
public Binding bindDeadExchangeAndQueue(@Qualifier("deadOrderQueue") Queue queue,
@Qualifier("deadOrderExchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("dead.order.#").noargs();
}
}
2、编写测试类:发送消息到MQ中
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestDelayQueue {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 测试延迟队列
*/
@Test
public void testSendStr() throws Exception{
// 发送消息
// 参数1:交换机名字
// 参数2:路由键
// 参数3:消息字符串
rabbitTemplate.convertAndSend(
"order_topic_exchange",
"order.news",
"用户下单成功,订单ID:1");
}
}
3.2、消息消费方代码实现
@Component
public class RabbitMQConfig {
/**
* 监听mq消息
*/
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "dead_order_topic_exchange",type = "topic"),
value = @Queue(value = "dead_order_queue",durable = "true"),
key = "dead.order.#"
))
public void handlerMessage(Message message, Channel channel)throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// 1. 转换消息
System.out.println(new String(message.getBody()));
// 2. 业务处理
System.out.println("执行业务处理...");
System.out.println("根据订单id查询状态...");
System.out.println("判断是否支付成功...");
System.out.println("取消订单,回滚库存...");
// 3. 手工签收
channel.basicAck(deliveryTag,true);
} catch (Exception e) {
// 4. 拒绝签收
channel.basicNack(deliveryTag,true,false);
}
}
}
四、延迟队列小结
- 延迟队列指消息进入队列后,可以被延迟一定时间再进行消费。
- RabbitMQ没有提供延迟队列功能,但可以使用:TTL+DLX实现延迟队列效果。