延迟队列存储的对象肯定是对应的延时消息,所谓"延时消息"是指当消息被发送以后,并不想让消费者立即拿到消息,而是等待指定时间后,消费者才拿到这个消息进行消费。
本人使用场景:
项目对接银行支付, 由于银行回调会由于某种原因如网络波动等情况导致没有接收到回调, 需要自己去查询该订单是否已经支付完成. 如在三分钟后查询支付详情.
消息的TTL就是消息的存活时间
RabbitMQ 可以针对 Queue 设置过期时间或者针对 message 设置过期时间, 如果超时(两者都设置以最先到期时间为准),消息则变为死信
针对 Queue 设置过期时间
创建一个枚举类
@Getter
public enum QueueEnum {
/**
* 死信队列
*/
BCM_SELECT_QUEUE_TTL("delay_exchange", "delay_queue_per_queue_ttl", "bcm.qrcode.issued"),
/**
* 延迟队列
*/
BCM_SELECT_QUEUE("delay_exchange", "delay_process_queue", "bcm.pay.select");
/**
* 交换名称
*/
private String exchange;
/**
* 队列名称
*/
private String name;
/**
* 路由键
*/
private String routeKey;
QueueEnum(String exchange, String name, String routeKey) {
this.exchange = exchange;
this.name = name;
this.routeKey = routeKey;
}
}
配置类
@Configuration
public class MessageRabbitMqConfiguration {
/**
* 死信队列 必须无人监听
* @return
*/
@Bean
public Queue delayQueuePerQueueTTL() {
Map<String,Object> arguments = new HashMap<>();
//交换机
arguments.put("x-dead-letter-exchange",QueueEnum.BCM_SELECT_QUEUE_TTL.getExchange());
//死信后转发到监听队列的路由键
arguments.put("x-dead-letter-routing-key",QueueEnum.BCM_SELECT_QUEUE.getRouteKey());
//过期时间
arguments.put("x-message-ttl",3 * 60 * 1000);
return new Queue(QueueEnum.BCM_SELECT_QUEUE_TTL.getName(), true, false, false,arguments);
}
/**
* 延迟队列
* @return
*/
@Bean
public Queue orderReleaseOrderQueue() {
return new Queue(QueueEnum.BCM_SELECT_QUEUE.getName(), true, false, false);
}
/**
* 交换机
* @return
*/
@Bean
public Exchange orderEventExchange() {
//String name, boolean durable, boolean autoDelete, Map arguments
return new TopicExchange(QueueEnum.BCM_SELECT_QUEUE.getExchange(),true,false);
}
/**
* 与死信队列路由绑定
* @return
*/
@Bean
public Binding orderCreateOrderBinding() {
return new Binding(QueueEnum.BCM_SELECT_QUEUE_TTL.getName(),
Binding.DestinationType.QUEUE,
QueueEnum.BCM_SELECT_QUEUE_TTL.getExchange(),
QueueEnum.BCM_SELECT_QUEUE_TTL.getRouteKey(),
null);
}
/**
* 与延迟队列绑定
* @return
*/
@Bean
public Binding orderReleaseOrderBinding() {
return new Binding(QueueEnum.BCM_SELECT_QUEUE.getName(),
Binding.DestinationType.QUEUE,
QueueEnum.BCM_SELECT_QUEUE.getExchange(),
QueueEnum.BCM_SELECT_QUEUE.getRouteKey(),
null);
}
}
发送消息
// 发送延迟队列 3 分钟 查询支付详情
BCMMessage message = new BCMMessage();
message.setMerTranNo(orderNo);
message.setTranType("PAY");
message.setCount(1);
//主要此处使用的是死信队列的路由键
rabbitMqTemplate.sendMessage(message, QueueEnum.BCM_SELECT_QUEUE_TTL.getRouteKey(), QueueEnum.BCM_SELECT_QUEUE_TTL.getExchange());
监听消息
@Component
@Slf4j
@AllArgsConstructor
@RabbitListener(queues = "delay_process_queue")
public class BcmMsgConsumer {
@RabbitHandler
public void bcmPaySelect(@Payload BCMMessage message) {
log.info("BcmMsgConsumer-bcmPaySelect-message: {}", message.toString());
// 业务处理代码
...
//如果还是失败继续尝试,共三次
if (message.getCount() < 3){
message.setCount(message.getCount() + 1);
rabbitMqTemplate.sendMessage(message, QueueEnum.BCM_SELECT_QUEUE_TTL.getRouteKey(), QueueEnum.BCM_SELECT_QUEUE_TTL.getExchange());
}
}
}
针对 message 设置过期时间 expiration : 180000
由于RabbitMQ采用的是惰性检查机制,也就是懒检查,如果第一个是5分钟过期, 第二个是3分钟过期,服务器拿出第一个消息发现是5分钟后过期,则5分钟后再来拿取,所以第二个设置的3分钟过期消息得在第一个消息过期之后才能过期