公司动态

JEECG-Boot 接口幂等性怎么落地:Token 机制与分布式锁两把钥匙,一次说清

📅 2026/8/31 12:49:14
JEECG-Boot 接口幂等性怎么落地:Token 机制与分布式锁两把钥匙,一次说清
JEECG-Boot 接口幂等性怎么落地Token 机制与分布式锁两把钥匙一次说清【免费下载链接】jeecg-boot【低代码v2.0一句话即可生成整个系统】企业级AI低代码平台一键生成前后端代码甚至整个系统。 AI Skills 一句话画流程、设计表单、生成报表、大屏。内置 AI应用平台涵盖AI聊天、知识库、流程编排、MCP插件等兼容主流大模型。引领AI低代码「Skills 生成 → 在线配置 → 代码生成 → 手工合并-AI修改」开发模式解决 Java 项目 90% 重复工作提高效率又不失灵活。项目地址: https://gitcode.com/GitHub_Trending/je/jeecg-boot先说个常见翻车现场用户双击提交订单网络刚好慢一拍同一个订单插了两次库或者 MQ 把扣款消息重投了一次钱扣了两遍。这就是接口幂等性要管的事——同一个请求打进来一次和打进来三次产生的结果必须一样。JEECG-Boot 里针对这类问题实际上准备了两种手段认证侧的 Token 校验机制和基于 Redisson 的分布式锁。这篇文章就带你把这两把钥匙分清楚各自能解什么锁、怎么接进来。一次双击和一次重投是两件不同的事新手最容易犯的错是把防重复当成一个整体方案去找其实它拆开后是两个问题问题一同一个客户端把同一个动作发了多次。双击、表单连点、超时重试来源都是人或端。你希望的是第二个请求根本别进来。问题二多个请求并发地改同一份数据。两个服务实例同时给库存 -1谁先谁后没人管数据就脏了。你希望的是同一时刻只有一个请求能碰它。这两种问题混在一起谈就会出现我上了锁怎么还重复提交这种扯不清的锅。下面分开看。Token 机制JEECG-Boot 里的 Token 到底管到哪一步先泼一盆冷水JEECG-Boot 的 Token 机制是登录态JWT的鉴权校验不是业务级的防重放 Token。它的入口在 TokenUtils.verifyToken核心逻辑就这几步public static boolean verifyToken(String token, CommonAPI commonApi, RedisUtil redisUtil) { if (StringUtils.isBlank(token)) { throw new JeecgBoot401Exception(token不能为空!); } String username JwtUtil.getUsername(token); if (username null) { throw new JeecgBoot401Exception(token非法无效!); } // ... 查用户、校验状态再走 jwtTokenRefresh 校验/续期 return true; }注意jwtTokenRefresh这一步Token 会存在 Redis 里校验时如果快过期就续期目的是让用户操作时不掉线。所以它的角色很明确——确认你是谁而不是确认这个动作能不能再做一次。那防重复提交谁来兜底老实说前端按钮防抖 后端分布式锁才是 JEECG-Boot 生态里实际扛住重复提交的主力。如果你需要一次性凭证先领 Token、提交即作废那种严格幂等设计需要自己在业务层加一层平台没有现成注解。这点心里有数省得照着网上文章找了一个不存在的 API。分布式锁接入三步走JEECG-Boot 的 Redisson 锁后端并发这层JEECG-Boot 给的是jeecg-boot-starter-lock基于 Redisson用法有注解式和编程式两种。仓库里现成的示例在 DemoLockTest.java三步接入第一步加依赖。在你的业务模块 pom 里引入dependency groupIdorg.jeecgframework.boot/groupId artifactIdjeecg-boot-starter-lock/artifactId /dependency第二步配 Redis。在application-dev.yml里已有现成的一段jeecg: lock: #分布式锁配置 redisson: address: 127.0.0.1:6379 password: type: STANDALONE enabled: true第三步用锁。注解式最简单适合定时任务这类同一时间只能跑一份的场景Scheduled(cron 0/5 * * * * ?) JLock(lockKey CloudConstant.REDISSON_DEMO_LOCK_KEY1) public void execute() throws InterruptedException { // 每 5 秒触发一次但同一时刻全集群只有一个实例在跑 }需要更细控制比如抢不到锁就跳过而不是排队就用编程式注入RedissonLockClient后if (redissonLock.tryLock(key, -1, expireSeconds)) { try { // 执行业务逻辑 } finally { redissonLock.unlock(key); } }这里tryLock第二个参数是等待时间-1表示不等待第三个是锁的持有时长。为什么必须带过期时间因为持锁的进程可能崩掉没有兜底的锁会把整个业务卡死。Redisson 内部有看门狗机制配合但业务侧显式传expireSeconds仍然是好习惯。两种方案怎么选一张表看完维度Token 机制登录态校验分布式锁Redisson解决的问题请求是否来自合法在线用户同一时刻谁允许碰这份数据作用范围全局鉴权所有接口锁粒度你定按 key 隔离挡得住重复提交吗挡不住它只管身份能挡住并发重复执行部署形态单体、微服务通用依赖 Redis天然跨实例典型失败姿势Token 过期用户被踢锁没释放、锁粒度太粗适用场景所有接口的准入控制库存扣减、订单号生成、定时任务互斥一句话记忆Token 管谁能进锁管谁能先动。防重复提交想彻底一点还得在前端把按钮做防抖后端用锁兜底三层一起上。容易踩的坑与落地建议锁粒度别贪大。用全局一把大锁保护整个订单流程等于把并发能力清零。key 里带上业务主键比如lock:order:{orderId}只锁该锁的那条数据。unlock 必须放进 finally。上面示例里那层try/finally不是仪式感业务抛异常时锁不释放后续请求全部卡死。等待时间和持锁时间是两回事。tryLock(key, -1, expire)里第一个是抢不到等多久第二个是抢到后最多拿多久。新手经常把-1当成持锁时间锁永远不自己放直接生产事故。别指望 Token 帮你幂等。再次强调JEECG-Boot 的 Token 是登录态。如果你的场景是消息重投要幂等要么在消费端用锁 去重表要么自己做一次性 Token别把希望寄托在鉴权 Token 上。先量再改。上锁前先确认瓶颈真的是并发冲突而不是 SQL 慢。锁解决不了慢查询反而把单线程慢放大成全员排队。最后给个务实的落地顺序前端防抖止血 → 后端JLock或tryLock兜住并发 → 对账/去重表做最终一致性核对。三件套齐了大部分重复提交和重复扣款的工单就再也不会来了。 【免费下载链接】jeecg-boot【低代码v2.0一句话即可生成整个系统】企业级AI低代码平台一键生成前后端代码甚至整个系统。 AI Skills 一句话画流程、设计表单、生成报表、大屏。内置 AI应用平台涵盖AI聊天、知识库、流程编排、MCP插件等兼容主流大模型。引领AI低代码「Skills 生成 → 在线配置 → 代码生成 → 手工合并-AI修改」开发模式解决 Java 项目 90% 重复工作提高效率又不失灵活。项目地址: https://gitcode.com/GitHub_Trending/je/jeecg-boot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考