ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

接口幂等怎么做才靠谱?Redis token + 数据库唯一键,我两层都上了

接口幂等怎么做才靠谱?Redis token + 数据库唯一键,我两层都上了 接口幂等怎么做才靠谱Redis token 数据库唯一键我两层都上了导读求职招聘系统里“重复提交是个高频事故源用户手抖点了两次投递简历”前端没拦住后端就插了两条投递记录重试机制误触发发布职位变成发布两次。接口幂等是后端必须扛住的活不能指望前端。这篇讲我在 qkl-boot 里落地的两层幂等方案。第一层Redis token 机制防正常重复提交思路是一单一令前端在提交前先向后端要一个 token提交时带上这个 token后端只认第一次用掉它的请求。RestControllerRequestMapping(/common)publicclassIdempotentController{AutowiredprivateStringRedisTemplateredis;/** 前端提交前先拿一个幂等令牌 */GetMapping(/idempotent-token)publicResultgetToken(){StringtokenUUID.randomUUID().toString();redis.opsForValue().set(idem:token,1,Duration.ofMinutes(10));returnResult.ok(token);}}关键在校验 删除必须原子——用 Redis 的 delete 判断返回值// 幂等校验切面简化版publicvoidcheckToken(Stringtoken){// delete 返回 1 说明 token 存在且被删除第一次返回 0 说明已被用过或不存在Booleanokredis.delete(idem:token);if(!Boolean.TRUE.equals(ok)){thrownewBizException(重复提交请勿频繁操作);}}delete本身是原子的多个请求同时来只有一个能删成功其余全部被拦。这是 token 方案的核心——校验和消费必须是一步。第二层数据库唯一键兜底防极端并发穿透Redis 方案在单实例下够用但极端并发下如果 Redis 短暂不可用或者 token 校验和业务执行之间出了问题还是可能穿透。真正的兜底是数据库层的唯一约束。以投递简历为例给投递记录表加唯一索引ALTERTABLEjob_deliveryADDUNIQUEKEYuk_user_job(user_id,job_id);业务层捕获唯一键冲突ServicepublicclassDeliveryService{publicvoiddeliver(LonguserId,LongjobId,StringidemToken){// 第一层Redis token 校验切面已做try{deliveryMapper.insert(userId,jobId);}catch(DuplicateKeyExceptione){// 第二层数据库唯一键兜底重复投递直接返回成功已存在log.info(重复投递已拦截: user{} job{},userId,jobId);return;}}}两层都上之后前端正常防抖走 token极端穿透由数据库唯一键接住重复数据从根上就插不进去。踩坑token 校验和业务执行不是原子的现象压测投递接口200 并发下出现几条重复投递记录。明明 token 校验用 delete 是原子的怎么会穿透排查看代码发现——token 校验在一个切面里Around前置校验 删除业务方法在切面之后执行。但业务方法内部又调了远程服务简历解析耗时较长token 在切面里已经被删了业务还没执行完。此时如果前端超时重试会重新拿新 token 再提交——新 token 校验当然通过业务又执行了一遍。定位问题不在 token 机制本身在超时重试的语义前端把接口超时误判为接口失败重新走了一遍完整流程拿新 token 提交。token 防的是同一 token 重复提交防不了重试后拿新 token 再提交。解决两层一起用。token 保证单次请求不被重复执行数据库唯一键保证同一个业务动作用户职位无论来几次都只有一条记录。后者才是业务幂等的真兜底。压测验证200 并发下数据库记录数精确等于岗位数零重复。可直接复用的清单token 的校验 删除必须原子用redis.delete()返回值判断别先 get 再 del唯一键冲突用DuplicateKeyException捕获重复操作按成功返回幂等语义token 设置过期时间10 分钟够了防止 token 堆积幂等 token 适合防重复提交业务幂等同人同动作靠数据库唯一键涉及远程调用的接口token 超时重试语义要想清楚别把超时当失败这套两层方案在 qkl-boot 里跑了大半年投递、发布、报名这些高频写接口全走它没再出过重复数据。核心一句话Redis 管单次数据库管业务谁也别指望单靠一层。项目源码https://gitee.com/gzqkl/qkl-boot
返回列表