要在利用中做到幂等,其实并不难,本文尝试做一个系统性的总结,欢送一起探讨。

什么是幂等

某个操作执行一次,跟执行屡次的成果一样。幂等一词来自于数学中的幂等,即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 {        // 稍后重试    }