TTL+死信队列实现延时队列:正常消息过期没有被消费掉,进入死信队列后立即消费。
1.pom.xml引用rabbitMQ
<!--rabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.application.yml引入rabbitMQ
rabbitmq:
port: 5672
host: localhost
username: admin
password: wdj123456
listener:
direct:
acknowledge-mode: manual
simple:
acknowledge-mode: manual
retry:
enabled: true
max-attempts: 5
initial-interval: 3000
virtual-host: newCore
publisher-confirm-type: correlated
publisher-returns: true
3.实现需求:到拼餐时间,没有任何用户参与进来,共享订单取消
RabbitMQ延时消息配置类
package com.hxnwm.ny.diancan.common.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* RabbitMQ延时消息配置
*
* @time 2022/3/11 11:40
*/
@Configuration
public class RabbitMQLazyConfig {
// ----- 共享订单未拼单成功 wdj-----
/**
* 共享订单立即消费队列
*/
public static final String ACT_SHAREORDER_QUEUE="act.shareOrder.queue";
/**
* 共享订单死信消费队列
*/
public static final String ACT_SHAREORDER_DELAY_QUEUE="act.shareOrder.delay.queue";
/**
* 共享订单交换器
*/
public static final String ACT_SHAREORDER_EXCHANGE="act.shareOrder.exchange";
/**
* 共享订单死信交换器
*/
public static final String ACT_SHAREORDER_DEAD_EXCHANGE="act.shareOrder.dead.exchange";
/**
* 共享订单立即消费路由键
*/
public static final String ACT_SHAREORDER_ROUTING_KEY = "act.shareOrder.routing.key";
/**
* 共享订单延迟路由键
*/
public static final String ACT_SHAREORDER_DELAY_ROUTING_KEY = "act.shareOrder.delay.routing.key";
/**
* 共享订单立即消费队列
*/
@Bean
public Queue actShareOrderQueue() {
return new Queue(ACT_SHAREORDER_QUEUE, true);
}
/**
* 延迟队列: 创建一个延迟队列, 此队列中的消息没有消费者去消费, 到了过期时间之后变成死信, 变死信之后会根据
* 绑定的DLX和routingKey重新发送到指定交换机再到指定队列。
*/
@Bean
public Queue delayShareOrderQueue() {
Map<String, Object> map = new HashMap<>();
map.put("x-dead-letter-exchange", ACT_SHAREORDER_EXCHANGE);
map.put("x-dead-letter-routing-key", ACT_SHAREORDER_ROUTING_KEY);
return new Queue(ACT_SHAREORDER_DELAY_QUEUE, true, false, false, map);
}
/**
* 声明立即消费队列交换机-direct类型
* 注:把消息投递到那些binding key与routing key完全匹配的队列中。
*/
@Bean
public DirectExchange actShareOrderExchange() {
return new DirectExchange(ACT_SHAREORDER_EXCHANGE, true, false);
}
/**
* 声明死信队列交换机-direct类型
*/
@Bean
public DirectExchange deadShareOrderLetterExchange() {
return new DirectExchange(ACT_SHAREORDER_DEAD_EXCHANGE, true, false);
}
/**
* 把立即消费的队列和立即消费交换机绑定, immediate_exchange, 路由键:immediate_routing_key
*/
@Bean
public Binding actShareOrderBinding() {
return BindingBuilder.bind(actShareOrderQueue()).to(actShareOrderExchange()).with(ACT_SHAREORDER_ROUTING_KEY);
}
/**
* 死信队列的交换机绑定:把延迟消费的队列和死信交换机绑定, immediate_dead_exchange, 路由键:delay_routing_key
*/
@Bean
public Binding queueShareOrderDeadBinding() {
return BindingBuilder.bind(delayShareOrderQueue()).to(deadShareOrderLetterExchange()).with(ACT_SHAREORDER_DELAY_ROUTING_KEY);
}
}
4.发送消息工具类
/**
* 订单工具类
*
* @author knight
* @time 2022/3/11 11:42
*/
@Component
@Slf4j
//设置scope为:prototype
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class OrderUtils {
@Resource
private AmqpTemplate amqpTemplate;
/**
*到拼餐时间,没有任何用户参与进来,共享订单取消
*
* @param shareOrder
* @return void
* @Author wdj
* @date 2023/3/21 16:23
*/
public void delayCancelShareOrder(ShareOrder shareOrder, long millisecond) {
this.amqpTemplate.convertAndSend(RabbitMQLazyConfig.ACT_SHAREORDER_DEAD_EXCHANGE, RabbitMQLazyConfig.ACT_SHAREORDER_DELAY_ROUTING_KEY, shareOrder, message -> {
//给消息设置延迟毫秒值
message.getMessageProperties().setExpiration(String.valueOf(millisecond));
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message;
});
log.info("把共享订单{}取消,发送到rabbitmq队列中", shareOrder.getShareOrderSn());
}
}
5.业务层:创建订单时,加入延时队列,设置消息过期时间
//将共享用餐订单添加到延时对列中
this.orderUtils.delayCancelShareOrder(shareOrder,(shareOrder.getShareTime()-now)*1000);
6.消息监听,并且消费
package com.hxnwm.ny.diancan.common.listener;
/**
* 延时任务监听
*
* @time 2022/3/11 10:40
*/
@Slf4j
@Component
public class LazyOrderListener {
@Resource
private OrderService orderService;
@Resource
private OrderManage orderManage;
/**
* 监听共享用餐成功后如果到了拼餐时间,还没有任何用户参与进来,自动给此共享订单详情页状态变为已取消状态
*
* @author wdj
* @time 2023/3/21 10:48
*/
@RabbitListener(queues = RabbitMQLazyConfig.ACT_SHAREORDER_QUEUE)
@Transactional(rollbackFor = Exception.class)
public void listenDelayCancelShareOrder(ShareOrder shareOrder1) {
log.info("监听到共享订单shareOrderSn:{} ", shareOrder1.getShareOrderSn());
try {
log.info("监听共享订单处理成功");
}catch (Exception e){
e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
}