使用redis zset + mq实现。

其实这个方案就是长延迟用 Redis,短延迟用 RocketMQ,既保证了高精度触发,又突破了 RocketMQ 延迟等级的限制。

实现思路

  1. 下单时
    • 把订单 ID 和到期时间(orderExpireTime)存到 Redis 的 ZSet,score 就是到期时间的时间戳(毫秒)。
  2. 定时扫描 Redis
    • 每隔一段时间(比如 1 分钟)扫描 score <= 当前时间 的订单 ID。
    • 把这些订单 ID 从 ZSet 删除。
    • 发 RocketMQ 延迟消息(如果还需要短延迟,比如多等几秒做缓冲)。
  3. RocketMQ 消费者
    • 收到消息执行订单关闭逻辑(更新订单状态、回滚库存等)。
  4. 幂等处理
    • 确保订单关闭是幂等的,避免重复执行。

Redis 数据结构

# ZSet key: order_timeout
ZADD order_timeout 1734144000000 orderId_123  # score 是到期时间戳

下单时写入 Redis

public void saveOrderToRedis(String orderId, long expireMillis) {
    String key = "order_timeout";
    long expireAt = System.currentTimeMillis() + expireMillis; // 过期时间戳
    redisTemplate.opsForZSet().add(key, orderId, expireAt);
}

定时任务扫描到期订单并发 MQ 消息

@Scheduled(fixedRate = 60000) // 每分钟扫描一次
public void scanAndSendToMQ() {
    String key = "order_timeout";
    long now = System.currentTimeMillis();
    Set<String> dueOrders = redisTemplate.opsForZSet().rangeByScore(key, 0, now);

    if (dueOrders != null && !dueOrders.isEmpty()) {
        // 删除这些订单
        redisTemplate.opsForZSet().remove(key, dueOrders.toArray());

        // 发到 RocketMQ 让消费者执行关闭
        for (String orderId : dueOrders) {
            sendCloseOrderMQ(orderId);
        }
    }
}

优化点

扫描频率:可以按分钟、5分钟等频率调整,看业务精度需求
批量发送:到期订单多时,批量发 MQ 消息
高可用:定时任务用分布式任务调度(如 XXL-JOB、Quartz 集群模式)
防重:订单关闭时先判断状态是否已关闭

作者:张三  创建时间:2025-08-13 15:00
最后编辑:张三  更新时间:2025-11-28 10:00