砍材农夫砍材农夫
  • 微信记账小程序
  • java
  • redis
  • mysql
  • 场景类
  • 框架类
  • vuepress搭建
  • hexo搭建
  • 云图
  • 常用工具

    • git
    • gradle
    • Zadig
    • it-tools
    • 开源推荐
    • curl
  • 大前端

    • nodejs
    • npm
    • webpack
    • 微信
    • 正则
    • uniapp
  • java

    • java基础
    • jdk体系
    • jvm
    • spring
    • spring_cloud
    • spring_boot
    • 分库分表
    • zookeeper
  • python

    • python基础
    • python高级
    • python框架
  • 算法

    • 算法
  • 网关

    • spring_cloud_gateway
    • openresty
  • 高可用

    • 秒杀
    • 分布式
    • 缓存一致
  • MQ

    • MQ
    • rabbitMQ
    • rocketMQ
    • kafka
  • 其它

    • 设计模式
    • 领域驱动(ddd)
  • 关系型数据库

    • mysql5.0
    • mysql8.0
  • 非关系型数据库

    • redis
    • mongoDB
  • 分布式/其他

    • ShardingSphere
    • 区块链
  • 向量数据库

    • M3E
    • OPEN AI
  • Jmeter
  • fiddler
  • wireshark
  • AI入门
  • AI大模型
  • AI插件
  • AI集成框架
  • 相关算法
  • AI训练师
  • 量化交易
  • gitee
  • github
  • infoq
  • osc
  • 砍材工具
  • 关于
  • 相关运营
  • docker
  • k8s
  • devops
  • nginx
  • 元宇宙
  • 区块链
  • 物联网
  • linux
  • webrtc
  • web3.0
  • gitee
  • github
  • infoq
  • osc
  • 砍材工具
  • 关于
  • 中考
  • 投资
  • 保险
  • 思
  • 微信记账小程序
  • java
  • redis
  • mysql
  • 场景类
  • 框架类
  • vuepress搭建
  • hexo搭建
  • 云图
  • 常用工具

    • git
    • gradle
    • Zadig
    • it-tools
    • 开源推荐
    • curl
  • 大前端

    • nodejs
    • npm
    • webpack
    • 微信
    • 正则
    • uniapp
  • java

    • java基础
    • jdk体系
    • jvm
    • spring
    • spring_cloud
    • spring_boot
    • 分库分表
    • zookeeper
  • python

    • python基础
    • python高级
    • python框架
  • 算法

    • 算法
  • 网关

    • spring_cloud_gateway
    • openresty
  • 高可用

    • 秒杀
    • 分布式
    • 缓存一致
  • MQ

    • MQ
    • rabbitMQ
    • rocketMQ
    • kafka
  • 其它

    • 设计模式
    • 领域驱动(ddd)
  • 关系型数据库

    • mysql5.0
    • mysql8.0
  • 非关系型数据库

    • redis
    • mongoDB
  • 分布式/其他

    • ShardingSphere
    • 区块链
  • 向量数据库

    • M3E
    • OPEN AI
  • Jmeter
  • fiddler
  • wireshark
  • AI入门
  • AI大模型
  • AI插件
  • AI集成框架
  • 相关算法
  • AI训练师
  • 量化交易
  • gitee
  • github
  • infoq
  • osc
  • 砍材工具
  • 关于
  • 相关运营
  • docker
  • k8s
  • devops
  • nginx
  • 元宇宙
  • 区块链
  • 物联网
  • linux
  • webrtc
  • web3.0
  • gitee
  • github
  • infoq
  • osc
  • 砍材工具
  • 关于
  • 中考
  • 投资
  • 保险
  • 思
  • 常见案例分析

    • 分布式ID
    • 分布式锁
    • 订单超时取消
    • 库存扣减问题
    • 幂等接口设计
    • 接口限流设计
    • 接口防刷设计
    • 多级缓存设计
  • 相关拓展知识

    • TCP和UDP区别
    • 签名算法
    • 身份认证令牌
    • 如何防止class类被反编译
    • 消息队列
    • maven如何统一指定jdk
    • maven常用插件
  • 算法|数据结构

    • 二叉树
    • 红黑树
    • 快速比较两棵树查找相同节点方法
  • 方案一: 定时检测(数据库)
    • demo
    • 优点和缺点
  • 方案二: 延迟消息队列
    • 优点和缺点
  • 方案三: 基于redis过期事件
    • demo
  • 方案四: 兜底策略

方案一: 定时检测(数据库)

demo

package com.jysemel.kcnf.todo.job;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Component
public class TodoDayWarnJob {


    private static final Map<String, String> accessTokenCache = new HashMap<>();

    // 使用 ReentrantLock
    private final ReentrantLock lock = new ReentrantLock();

    @SneakyThrows
    @Scheduled(cron = "0 0/1 * * * ?")
    public void run() {
        // 获取锁,避免重复执行
        if (lock.tryLock()) {
            log.info("每日待办事项提醒.........");
            try {

            } finally {
                // 释放锁
                lock.unlock();
            }
        } else {
            log.info("任务正在执行中,跳过本次执行");
        }
    }
}

优点和缺点

小项目首选、快速
缺点: 耗时、并发问题

方案二: 延迟消息队列

Image text

优点和缺点

小项目首选、快速
缺点: 耗时、并发问题

方案三: 基于redis过期事件

基于redis过期key
开启 Redis 的过期事件通知(默认关闭)
redis.config.notify-keyspace-events "Ex"

demo

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;

@Configuration
public class RedisConfig {

    /**
     * 配置 Redis 消息监听容器
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(
            RedisConnectionFactory connectionFactory,
            MessageListenerAdapter listenerAdapter) {

        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);

        // 订阅所有数据库的过期事件
        container.addMessageListener(listenerAdapter, new PatternTopic("__keyevent@*__:expired"));

        return container;
    }

    /**
     * 绑定自定义的监听器
     */
    @Bean
    public MessageListenerAdapter listenerAdapter(OrderExpiredListener listener) {
        return new MessageListenerAdapter(listener);
    }
}

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;

@Component
public class OrderExpiredListener implements MessageListener {

    private static final Logger log = LoggerFactory.getLogger(OrderExpiredListener.class);

    @Override
    public void onMessage(Message message, byte[] pattern) {
        // 获取过期的 key
        String expiredKey = message.toString();
        log.info("收到 Redis 过期事件,key: {}", expiredKey);

        // 判断是否是订单 key(格式 order:{id})
        if (expiredKey != null && expiredKey.startsWith("order:")) {
            String orderId = expiredKey.substring(6); // 去掉 "order:"
            // 执行订单取消逻辑(幂等处理)
            cancelOrder(orderId);
        }
    }

    /**
     * 模拟取消订单:更新订单状态为已取消,打印日志
     */
    private void cancelOrder(String orderId) {
        // 实际项目中可注入 Service 操作数据库
        log.info("订单 {} 超时,已自动取消", orderId);
        // 这里可以调用数据库更新语句,例如 update orders set status = 'CANCELLED' where id = ? and status = 'NEW'
    }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 创建订单,设置过期时间(秒)
     * 例如:http://localhost:8080/order/create?orderId=1001&ttl=10
     */
    @GetMapping("/create")
    public String createOrder(@RequestParam String orderId,
                              @RequestParam(defaultValue = "30") long ttl) {
        // 模拟将订单信息存入数据库,状态为 NEW
        // ...

        // 将订单 ID 存入 Redis,设置过期时间
        String key = "order:" + orderId;
        redisTemplate.opsForValue().set(key, "待支付", ttl, TimeUnit.SECONDS);

        return "订单 " + orderId + " 已创建," + ttl + " 秒后过期自动取消";
    }
}

方案四: 兜底策略

幂等性:所有取消操作必须幂等,防止重复取消导致数据错误。
状态检查:执行取消前应检查订单当前状态(如仅当状态为“待支付”时才取消)。
重试机制:对于执行失败的取消操作,应有重试机制(如结合消息队列重试)。
监控告警:对长时间未处理的订单进行监控,及时发现策略失效。
最近更新: 2026/2/27 16:40
Contributors: kcnf
Prev
分布式锁
Next
库存扣减问题