原创 不码不疯魔 不码不疯魔 2023-08-28 22:48 发表于四川
收录于合集
#支付1个
#TTL1个
#延迟队列3个
#高频面试题9个
#取消订单3个
20
23
不疯魔不成活,大家好呀,我是科哥,江湖ID 不码不疯魔
大家好!今天我们将一起探讨一个高级Java技术话题——基于RabbitMQ的TTL(延迟队列+死信队列),实现支付订单超时自动取消。这个主题对于许多企业级应用来说都非常重要,因为它可以帮助我们解决一些具有挑战性的业务问题。
什么是延时队列?首先它要具有队列的特性,再给它附加一个延迟消费队列消息的功能,延迟队列相对比普通队列,区别就在延时的特性上,
普通队列先进先出,按入队顺序进行处理,而延时队列中的元素在入队时会指定一个延时时间,希望能够在指定时间到了以后处理。
在现实生活中,很多场景都需要用到延迟队列。以下就是使用延迟队列的企业级实战场景:
业务类场景:
1. 支付订单成功后,指定时间以后将支付结果通知给接入方。
2. 淘宝订单业务:下单之后如果三十分钟之内没有付款就自动取消订单。
3. 美团或饿了吧订餐通知:下单成功后60s之后给用户发送短信通知。
4. 会议预定系统,在预定会议开始前半小时通知所有预定该会议的用户。
5. IT问题工单超过24小时未处理,则自动拉企微群提醒相关责任人。
6. 用户下单外卖以后,距离超时时间还有10分钟时提醒外卖小哥即将超时。
技术框架场景:
1. 关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。
2. 缓存。缓存中的对象,超过了空闲时间,需要从缓存中移出。
3. 任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求等。
企业实战案例
假设我们正在开发一个电商网站,用户可以在该网站上购买商品。为了提供更好的用户体验,我们在用户下单后60s未支付,这时,我们可以通过RabbitMQ的TTL(延迟队列+死信队列)超时未支付自动取消订单
实现步骤如下:
1. 创建普通队列:命名为'order.release.order.queue',用于处理正常的支付订单。
2. 创建延迟队列:命名为'order.create.order',并设置延迟时间(例如60秒),用于存放需要超时处理的订单。
3. 创建死信队列:命名为'order.delay.queue',用于存放处理失败或超时的订单。
4.构建交换机对象:将'order.create.order的死信交换器设置为'
order.event.exchange',以便在订单超时时将订单发送到'order.delay.queue'队列。
5.发布订单消息:将支付订单的消息发布到'order.release.order.queue'队列,由消费者进行正常的处理。
6. 发布延迟消息:将需要超时处理的订单消息发布到'order.delay.queue'队列,并设置消息的TTL(Time To Live),即消息在队列中的存活时间。当消息存活时间达到预设的超时时间时,消息将被发送到'order.release.order'队列。
7. 处理超时订单:在'order.delay.queue'队列中,消费者在处理订单时需要判断订单是否已经超时。如果订单已经超时,则将订单消息发布到'order.release.order.queue'队列中,由专门的处理程序进行后续的取消操作。
通过以上步骤,基于RabbitMQ的TTL(延迟队列+死信队列)可以实现支付订单的超时自动取消功能。在实现过程中需要根据具体的业务需求进行调整和优化,以保证系统的稳定性和可靠性。
由于篇幅和格式的限制,我无法在这里提供完整的代码。但我可以为您提供部分代码的示例,以展示如何实现基于RabbitMQ的TTL(延迟队列+死信队列),实现支付订单超时自动取消。
@Configuration
public class OrderMqConfig {
//死信队列
private static final String DELAY_QUEUE = "delay_queue";
//正常队列
private static final String NORMAL_QUEUE = "normal_queue";
//死信交换器
private static final String DELAY_EXCHANGE = "delay_exchange";
//死信路由键
private static final String DELAY_KEY = "delay_key";
// 构建一个死信队列对象
@Bean
public Queue orderDelayQueue(){
//构造参数
Map<String,Object> arguments = new HashMap<>();
//为死信队列设置队列交换器:order.event.exchange
arguments.put("x-dead-letter-exchange","order.event.exchange");
//为死信队列设置队列路由key:order.release.order
arguments.put("x-dead-letter-routing-key","order.release.order");
//为死信队列中的消息设置过期时间:1 分钟
arguments.put("x-message-ttl",60000);
/**
* String name, 队列名字
* boolean durable, 队列是否持久化
* boolean exclusive, 队列是否排他的
* boolean autoDelete, 队列是否删除
* @Nullable Map<String, Object> arguments 自定义属性
*/
// 构建对象,塞入相关参数
Queue queue = new Queue("order.delay.queue",
true,
false,
false,
arguments
);
return queue;
}
//构建普通的队列
@Bean
public Queue orderReleaseOrderQueue(){
// 构建对象,塞入相关参数
Queue queue = new Queue("order.release.order.queue",
true,
false,
false,
null
);
return queue;
}
// 构建交换机对象
@Bean
public Exchange orderEventExchange(){
/**
* String name, 交换机名称
* boolean durable, 队列是否持久化
* boolean autoDelete, 队列是否删除
*/
TopicExchange exchange = new TopicExchange(
"order.event.exchange",
true,
false);
return exchange;
}
// 将交换机与死信队列绑定
@Bean
public Binding orderCreateOrderBinding(){
/**
* String destination, 目的地
* DestinationType destinationType, 目的地类型
* String exchange, 交换机
* String routingKey, 路由key
* @Nullable Map<String, Object> arguments 参数
*/
Binding binding = new Binding("order.delay.queue",
Binding.DestinationType.QUEUE,
"order.event.exchange",
"order.create.order",
null
);
return binding;
}
// 将交换机与普通队列绑定
@Bean
public Binding orderReleaseOrderBinding(){
/**
* String destination, 目的地
* DestinationType destinationType, 目的地类型
* String exchange, 交换机
* String routingKey, 路由key
* @Nullable Map<String, Object> arguments 参数
*/
Binding binding = new Binding("order.release.order.queue",
Binding.DestinationType.QUEUE,
"order.event.exchange",
"order.release.order",
null
);
return binding;
}
}
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Resource
private OrderMapper orderMapper;
@Resource
private RabbitTemplate rabbitTemplate;
@Override
public RespResult createOrder(OrderCreateDTO orderCreateDTO, HttpServletResponse response) {
OrderInfo orderInfo = new OrderInfo();
orderInfo.setId(String.valueOf(DCGenIdUtil.genIdBySnowflake()));
orderInfo.setOrderTime(new Date());
String orderNo = GenerateSequenceUtil.generateSequenceNo();//20230404160605001
if (!StringUtils.isEmpty(orderNo)){
orderInfo.setOrderNo(orderNo);
}
orderInfo.setStatus(Constant.ORDER_CREATE);
//拷贝对象(只拷贝不为空的属性)
DCBeanUtil.copyNotNull(orderInfo, orderCreateDTO);
//保存订单
orderMapper.saveOrderInfo(orderInfo);
orderCreateDTO.setOrderNo(orderInfo.getOrderNo());
//将订单保存到延迟队列中
rabbitTemplate.convertAndSend("order.event.exchange","order.create.order",orderInfo);
return RespResult.success(orderCreateDTO);
}
/**
* 功能:job处理超时的订单
*/
@Override
public void cancelOrderForJob() {
OrderReqDTO reqDTO = new OrderReqDTO();
reqDTO.setStatus(Constant.ORDER_CREATE);
reqDTO.setIsCancel(1);
//查询前刚产生的100条,不要整表查,不然速度太慢
//reqDTO.setLimitRecords(100);
List<OrderInfo> orderList = orderMapper.findList(reqDTO);
if(CollUtil.isEmpty(orderList)){
log.warn("没有自动取消的订单!时间:{}", LocalDateTime.now());
return;
}
//并行流
orderList.parallelStream().forEach(order->{
OrderCancelDTO orderCancelDTO = new OrderCancelDTO();
orderCancelDTO.setOrderNo(order.getOrderNo());
orderCancelDTO.setMobile(order.getMobile());
this.cancelOrder(orderCancelDTO);
});
log.info("自动取消订单完成!共处理{}单,时间:{}", orderList.size(), LocalDateTime.now());
}
//修改订单支付状态
@Override
public RespResult updataOrderStatusByOrderNo(String out_trade_no) {
orderMapper.updataOrderStatusByOrderNo(out_trade_no,new Date(),Constant.ORDER_PAY);
return RespResult.success("支付成功");
}
/**
* 功能:取消订单
*/
public RespResult<Void> cancelOrder(OrderCancelDTO orderCancelDTO) {
//再根据手机和订单号再查询一遍
List<OrderInfo> orderList = orderMapper.findByMobileAndOrderNo(orderCancelDTO.getMobile(),
orderCancelDTO.getOrderNo());
if(CollUtil.isEmpty(orderList)){
return RespResult.error(String.format("取消订单失败,订单%s不存在", orderCancelDTO.getOrderNo()));
}
//取第一条
OrderInfo orderInfo = orderList.get(0);
if(Constant.ORDER_CREATE != orderInfo.getStatus()){
return RespResult.error("只能取消已下单状态的订单");
}
//时间向以后推移1分钟
orderInfo.setCancelOrderTime(DateUtil.offsetMinute(orderInfo.getOrderTime(), 1));
orderInfo.setStatus(Constant.ORDER_CANCEL);
//更新订单信息
orderMapper.updateById(orderInfo);
return RespResult.success();
}
}
点击下方公众号进入关注,后台回复【TTL】即可获取如上图所有源码,大量的商用级方案已经更新,记得关注,免费领取,标记星号哟!!!