要在利用中做到幂等,其实并不难,本文尝试做一个系统性的总结,欢送一起探讨。
什么是幂等
某个操作执行一次,跟执行屡次的成果一样。幂等一词来自于数学中的幂等,即f(f(x)) = f(x)。
须要保障幂等的场景
查问类的读操作,人造是幂等的,屡次调用不会有副作用。需思考以下几种写操作的状况:
- 调用上游写接口
- 写数据库、写Redis等
- 音讯订阅和解决
例子:不能给用户反复发放优惠券、现金处分、告诉等,商家更新商品时不能反复减少或缩小库存。
上面别离探讨这几种状况。
1、调用上游写接口
次要依附上游服务保障幂等。
本服务能做的是,在调上游写接口时不做重试,需设置重试次数为0。
2、本人服务保障
2.1 基于状态的幂等
这种状况比较简单,只有当满足前置条件时才容许操作,否则不容许更新(例如曾经是终态),间接返回。
例子:订单领取胜利后,不容许反复领取。
2.2 基于惟一键的幂等
幂等key的选取
与业务强相干,能够是商品id、订单id、用户id,或者日期等,或者是几个业务字段的组合。
几个例子:
- 一个用户每天只能领一张优惠券,通过 用户id+优惠券类型+日期字符串 即可惟一标识
- B端更新库存,商品id+该商品的版本号
- C端扣库存,订单id
值得注意的是,须要辨别新增和批改:批改时的幂等key往往须要带上版本号,能力辨别是否同一次批改,每次批改对应一个惟一的版本号。
实现形式
MySQL表中为幂等key建设惟一索引:强幂等,例如资金、订单,相对不容许反复解决,当插入反复数据时报错。
不举荐用Redis实现幂等,一旦Redis出问题,比方节点宕机,可能呈现2个client同时获取到锁的状况。
MySQL幂等伪代码:
插入重复记录,捕捉异样,提醒幂等拦挡。
try { // 插入记录 someDao.create(someRecord); } catch (DataIntegrityViolationException e) { // 如果是重复记录,返回异样 return failResponse("幂等拦挡"); } catch (Throwable t) { // 异样解决 return failResponse("其余异样"); }
3、音讯订阅和解决
MQ通常会保障音讯至多发送一次(可能屡次),并且在机器实例重启或发版时,consumer group会做rebalance,进而收到反复的音讯。因而,音讯的幂等解决必不可少。
实现形式:
在解决音讯前加上Redis锁:如果上锁胜利,则持续解决,否则稍后重试。
- setnxex,不存在时才设置,时效即为锁的租期,否则疏忽
- 接下来的业务解决,如果是本身逻辑须要强幂等则应用上述数据库幂等形式,如果全副依赖上游则依赖上游实现幂等
Redis幂等伪代码:
// 生成幂等key String redisKey = buildRedisKey(); // 上Redis锁,租期为leaseTime if (redisLock.tryLock(redisKey, leaseTime)) { // 业务逻辑解决 } else { // 稍后重试 }