首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

项目中,如何实现订单到期关闭?

  • 25-04-23 18:18
  • 3040
  • 13412
juejin.cn

一、技术方案组合(推荐)

1. 延迟消息 + 定时任务补偿

  • 核心流程使用消息队列延迟消息
  • 定时任务做兜底补偿
  • 结合分布式锁保证幂等性

2. Redis过期监听(可选补充)

  • 作为辅助手段提供快速响应
  • 不单独作为核心方案

二、详细实现步骤

1. 下单时发送延迟消息

java
代码解读
复制代码
// 订单创建成功后发送延迟消息 public void createOrder(Order order) { orderService.save(order); // 发送30分钟延迟消息(RocketMQ示例) Message message = MessageBuilder.withPayload(new OrderCloseMsg(order.getId())) .setHeader(RocketMQHeaders.DELAY_TIME_LEVEL, 3) // 对应30分钟级别 .build(); rocketMQTemplate.send(message); }

2. 消息消费者处理

java
代码解读
复制代码
@RocketMQMessageListener(topic = "ORDER_CLOSE_TOPIC", consumerGroup = "order_close_group") public class OrderCloseConsumer implements RocketMQListener { @Override @Transactional public void onMessage(OrderCloseMsg message) { // 获取分布式锁 String lockKey = "order_close_lock:" + message.getOrderId(); try { if (redisLock.tryLock(lockKey, 30, TimeUnit.SECONDS)) { Order order = orderService.getById(message.getOrderId()); // 检查订单状态 if (order != null && order.getStatus() == OrderStatus.UNPAID) { closeOrder(order); } } } finally { redisLock.unlock(lockKey); } } private void closeOrder(Order order) { // 1. 更新订单状态 order.setStatus(OrderStatus.CLOSED); orderService.updateById(order); // 2. 释放库存(分布式事务方案) inventoryService.unlockStock(order.getSkuId(), order.getQuantity()); // 3. 记录操作日志 orderLogService.logOperation(order.getId(), "AUTO_CLOSE"); } }

3. 定时补偿任务(Spring Scheduler示例)

java
代码解读
复制代码
@Scheduled(cron = "0 0/10 * * * ?") // 每10分钟执行 public void compensateCloseOrders() { // 查询30-40分钟前的未支付订单(留出10分钟缓冲) Date endTime = DateUtils.addMinutes(new Date(), -30); Date startTime = DateUtils.addMinutes(endTime, -10); List unpaidOrders = orderMapper.selectUnpaidOrders(startTime, endTime); unpaidOrders.forEach(order -> { // 使用线程池异步处理 asyncExecutor.execute(() -> { // 复用消息消费者的关闭逻辑 OrderCloseMsg msg = new OrderCloseMsg(order.getId()); new OrderCloseConsumer().onMessage(msg); }); }); }

4. Redis过期补充方案(可选)

java
代码解读
复制代码
// 订单创建时设置Redis key redisTemplate.opsForValue().set( "order_close:" + orderId, "1", 30, TimeUnit.MINUTES ); // 配置Redis过期监听 @Configuration public class RedisKeyExpirationConfig { @Bean public RedisMessageListenerContainer container(RedisConnectionFactory factory) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(factory); container.addMessageListener(expirationListener(), new PatternTopic("__keyevent@0__:expired")); return container; } @Bean public MessageListener expirationListener() { return (message, pattern) -> { String expiredKey = new String(message.getBody()); if (expiredKey.startsWith("order_close:")) { String orderId = expiredKey.split(":")[1]; // 提交到线程池处理(避免阻塞监听线程) asyncExecutor.execute(() -> processExpiredOrder(orderId)); } }; } }

三、关键注意事项

  1. 幂等性设计
  • 使用数据库行锁(SELECT FOR UPDATE)
  • 更新时检查状态:update orders set status='CLOSED' where id=#{id} and status='UNPAID'
  • 分布式锁(Redis或Redisson)
  1. 数据一致性
  • 采用最终一致性方案
  • 重要操作记录日志(可考虑事件溯源)
  • 使用本地事务表保证关键操作
  1. 性能优化
  • 分库分表处理海量订单
  • 补偿任务使用分页查询(limit 1000)
  • 异步线程池处理关闭操作
  1. 监控报警
  • 记录消息消费延迟情况
  • 监控定时任务执行情况
  • 设置订单关闭失败报警阈值
  1. 兜底方案
  • 每日对账任务检查异常订单
  • 提供管理后台手动关闭接口
  • 保留72小时内的订单关闭日志

四、架构图

sql
代码解读
复制代码
+-------------+ +---------------+ +---------------+ | Order | | Message | | Redis | | Service +---->+ Queue +---->+ (Optional) | +-----+-------+ +-------+-------+ +-------+-------+ | | | | | | +-----v-------+ +-------v-------+ +-------v-------+ | Scheduled | | Consumer | | Key | | Task | | Service | | Expiration | +-------------+ +---------------+ +---------------+ ↓ ↓ ↓ +-------------------------------------------------------+ | Database & Lock Service | +-------------------------------------------------------+

五、扩展优化建议

  1. 动态关闭时间
  • 在订单表中增加close_time字段
  • 延迟消息时间根据业务规则动态计算
  1. 关闭前提醒
  • 在到期前5分钟发送提醒通知
  • 使用多级延迟消息(25分钟提醒+30分钟关闭)
  1. 流量控制
  • 消息消费端采用令牌桶限流
  • 数据库更新操作批量处理
  1. 多级缓存
  • 使用本地缓存+Redis缓存订单状态
  • 减少数据库查询压力

这种组合方案可以保证在日均百万订单量的情况下,实现:

  • 99.95%的订单能在30±1分钟内关闭
  • 系统资源消耗降低60%以上
  • 人工干预需求减少90%以上
注:本文转载自juejin.cn的雷渊的文章"https://juejin.cn/post/7496048386413412371"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

103
后端
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top