原理
延时队列,消息放入队列中后,过了指定时间才被listener监听消费。
使用这种方式实现延时队列,我们首先要理清楚2个概念,TTL和死信队列。
一、TTL
TTL 全称 Time To Live(存活时间/过期时间)。当消息到达存活时间后,还没有被消费,会被自动清除。RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间。可以类比redis的TTL。
二、死信队列
死信队列,英文缩写:DLX 。Dead Letter Exchange(死信交换机)。
那如何让一个普通的队列成为死信队列呢?
消息成为死信的三种情况:
(1) 队列消息长度到达限制;
(2)消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
(3) 原队列存在消息过期设置,消息到达超时时间未被消费;
此处让消息变成死信使用的是第三种情况。
给普通消息设置过期时间,过期后消息进入指定的死信队列被消费。
代码实现
1、添加springboot对amqp的支持
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、mq配置类
@Configuration
public class CancelOrderMqConfig {
// 普通队列
public static final String CANCEL_ORDER_EXCHANGE = "hyb3.order.exchange";
public static final String CANCEL_ORDER_QUEUE = "hyb3.order.queue";
public static final String CANCEL_ORDER_ROUTINGKEY = "hyb3.order.routingkey";
// 死信队列
public static final String CANCEL_ORDER_DEAD_EXCHANGE = "hyb3.order.dead.exchange";
public static final String CANCEL_ORDER_DEAD_QUEUE = "hyb3.order.dead.queue";
public static final String CANCEL_ORDER_DEAD_ROUTINGKEY = "hyb3.order.dead.routingkey";
// 声明普通交换机
@Bean
public DirectExchange normalExchange() {
return new DirectExchange(CANCEL_ORDER_EXCHANGE);
}
// 声明死信交换机
@Bean
public DirectExchange deadExchange() {
return new DirectExchange(CANCEL_ORDER_DEAD_EXCHANGE);
}
// 创建普通队列QUEUE
@Bean
public Queue normalQueue() {
Map<String, Object> args = new HashMap<>(3);
// 声明当前队列绑定的死信交换机
args.put("x-dead-letter-exchange", CANCEL_ORDER_DEAD_EXCHANGE);
// 声明当前队列的死信路由 key
args.put("x-dead-letter-routing-key", CANCEL_ORDER_DEAD_ROUTINGKEY);
// 声明队列的 TTL
args.put("x-message-ttl", 5000); // 毫秒 5000毫秒 = 5s
return QueueBuilder.durable(CANCEL_ORDER_QUEUE).withArguments(args).build();
}
// 把普通队列绑定到普通交换机上
@Bean
public Binding queueBindingNormalExchange() {
return BindingBuilder.bind(normalQueue()).to(normalExchange()).with(CANCEL_ORDER_ROUTINGKEY);
}
// 声明死信队列
@Bean
public Queue deadQueue() {
return new Queue(CANCEL_ORDER_DEAD_QUEUE);
}
// 把死信队列绑定到死信交换机上
@Bean
public Binding deadLetterBindingQAD() {
return BindingBuilder.bind(deadQueue()).to(deadExchange()).with(CANCEL_ORDER_DEAD_ROUTINGKEY);
}
}
3、发送消息到队列中
@RestController
@RequestMapping("/weChatPay")
public class WeChatPayController {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostMapping("/testDelayMQ")
public void testDelayMQ(String param) {
rabbitTemplate.convertAndSend(CancelOrderMqConfig.CANCEL_ORDER_EXCHANGE, CancelOrderMqConfig.CANCEL_ORDER_ROUTINGKEY, param);
}
}
4、listener监听消息并处理
@Component
public class DLXReceiver {
@RabbitListener(queues = CancelOrderMqConfig.CANCEL_ORDER_DEAD_QUEUE)
@RabbitHandler
public void handler(Message message) {
String string = new String(message.getBody());
System.out.println("死信队列中接受到的消息:" + string);
// 业务处理
}
}