乐趣区

关于接口设计:接口服务中的幂等性设计和防重保证详细分析幂等性设计几种实现方法

什么是幂等性

  • 幂等性定义:

    • 一次和屡次申请某一个资源对于资源自身应该具备同样的后果
    • 任意屡次执行对资源自身所产生的影响均与一次执行的影响雷同
  • 幂等性定义的几个重点:

    • 幂等不仅仅只是一次或者屡次申请对资源没有副作用

      • 比方, 查询数据库操作, 没有增删改, 无论多少次操作对数据库都没有任何影响
    • 幂等还包含第一次申请的时候对资源产生了副作用, 然而当前的屡次申请都不会再对资源产生副作用
    • 幂等关注的是当前屡次申请是否对资源产生副作用, 并不关注后果
    • 网络超时等问题, 不是幂等的探讨范畴
  • 幂等性是零碎服务对外一种承诺, 而不是实现
  • 承诺只有调用接口胜利, 内部屡次调用对系统的影响是统一的
  • 申明为幂等的服务会认为内部调用失败是常态, 并且失败后必然会有重试

    幂等性的应用场景

  • 业务开发中, 常常遇到反复提交的状况:

    • 因为网络问题无奈收到申请后果而从新发动申请
    • 前端的操作抖动而造成的反复提交的状况
  • 在交易系统中, 领取零碎这种反复提交造成的问题尤为显著:

    • 用户在 APP 上间断点击屡次提交订单, 后盾应该只产生一个订单
    • 向领取零碎发动申请, 因为网络问题或者零碎 Bug 问题导致重发, 领取零碎应该只做一次扣除操作
  • 申明幂等的服务认为, 内部调用者会存在屡次调用的状况, 为了避免内部屡次调用对系统的数据状态产生屡次扭转, 须要将服务设计为幂等

    幂等和防重

  • 反复提交的状况和服务幂等的初衷是不同的

    • 反复提交是在第一次申请曾经胜利的状况下 , 人为地进行屡次操作, 导致不满足幂等要求的服务屡次扭转状态
    • 幂等更多应用的状况是第一次申请因为某些状况, 不如超时, 而导致不晓得后果或者申请失败的 异常情况下, 发动屡次申请
  • 幂等的目标是申请屡次确认第一次申请胜利, 不会因为屡次申请而呈现屡次的状态变动

    保障幂等性的状况

  • 在 SQL 中, 有以下三种场景, 只有第三种场景须要保障幂等性:

    • SELECT col1 FROM tab1 WHERE col2=2 : 无论执行多少次都不会扭转状态, 是人造的幂等
    • UPDATE tab1 SET col1=1 WHERE col2=2 : 无论执行胜利多少次状态都是统一的, 也是幂等操作
    • UPDATE tab1 SET col1=col1+1 WHERE col2=2: 每次执行的后果都会发生变化, 这种不是幂等的, 要采取策略保障幂等性

      设计幂等性服务

  • 幂等使得客户端逻辑解决很简略, 然而服务端逻辑会很简单
  • 满足幂等性服务须要蕴含两点逻辑:

    • 首先去查问上一次的执行状态, 如果没有则认为是第一次申请
    • 在服务扭转状态的业务逻辑前保障防反复提交的逻辑

      保障幂等策略

  • 幂等须要通过 惟一的业务单号 来保障:

    • 雷同的业务单号, 认为是同一业务
    • 应用惟一的业务单号确保: 前面屡次雷同业务单号的解决逻辑和执行成果是统一的
  • 幂等实现示例 - 领取:

    • 先查问订单是否领取过
    • 如果曾经领取过, 返回领取胜利
    • 如果没有领取, 则进行领取流程, 批改订单的状态为已领取

      防反复提交策略

  • 在保障幂等的策略中, 执行是分两步执行的, 前面一步依赖下面一步的查问后果, 这样就无奈保障原子性
  • 无奈保障原子性在高并发的状况下会存在问题:

    • 第二次申请在第一次申请的下一步订单状态没有批改为 ” 已领取状态 ” 时进行
    • 为了解决这个问题 : 将查问和变更状态操作加锁, 并将并行操作改为串行执行

      乐观锁
  • 如果只是更新已有的数据, 没有必要对业务进行加锁
  • 设计表构造时应用乐观锁, 个别通过 version 来实现乐观锁:

    • 保障执行效率
    • 保障幂等

      UPDATE tab1
      SET    col1=1,version=version+1
      WHERE    version=#version# 

      因为 ABA 问题会导致乐观锁存在生效的状况, 只有保障 version 值自增就不会呈现 ABA 的问题

      防重表
  • 应用 orderNo 作为去重表中的惟一索引, 每次申请都依据订单号 orderNo 向去重表中插入一条数据:

    • 第一次申请查问订单领取状态:

      • 订单没有领取
      • 进行领取操作
      • 无论胜利与否, 执行实现之后更新订单的状态为胜利或失败, 删除去重表中的数据
      • 后续订单因为表中的惟一索引插入失败, 返回操作失败, 直到第一次申请实现(胜利或者失败)
  • 防重表的作用是实现加锁的性能

    分布式锁
  • 能够应用 Redis 分布式锁代替防重表的性能
  • 示例:

    • 订单发动领取申请
    • 领取零碎会去 Redis 缓存中查问是否存在该订单Key
    • 如果不存在, 向 Redis 中减少 Key 为订单号
    • 查问订单领取是否曾经领取
    • 如果没有则进行领取, 领取实现后删除该订单的Key
  • 通过 Redis 实现分布式锁, 只有这次订单申请实现, 下次申请才会进来
  • 比照去重表,Redis 分布式锁将放并发做在缓存中, 效率更高
  • 同一时间只能实现一次领取申请

    token 令牌
  • token 令牌分为两个阶段:

    • 申请 token 阶段:

      • 在进入到提交订单页面之前, 须要订单零碎依据用户信息向领取零碎发动一次申请 token 的申请
      • 领取零碎将 token 保留到 Redis 缓存中, 给领取阶段应用
    • 领取阶段:

      • 订单零碎获取到申请的token, 发动领取申请,
      • 领取零碎查看 Redis 是否存在该token

        • 如果存在, 示意第一次发动领取申请, 删除缓存中的 token 开始领取逻辑解决
        • 如果缓存中不存在, 示意非法申请

          领取缓冲区
  • 领取缓冲区:

    • 将订单的领取申请都疾速地接管下来, 是一个疾速接管申请的缓冲管道
    • 应用 异步 工作解决管道中的数据, 过滤调掉反复的待领取的数据
  • 长处: 同步转异步, 高吞吐
  • 毛病: 无奈及时返回领取后果, 须要后续监听领取后果的异步返回

  • 幂等是为了简化客户端逻辑, 然而减少了服务提供者的逻辑和老本
  • 幂等的应用须要依据具体场景具体分析
  • 减少了额定管制幂等的业务逻辑, 简单了业务性能
  • 将并行的性能转化为串行, 升高了执行效率

退出移动版