zoukankan      html  css  js  c++  java
  • 【接口设计】高并发下的接口幂等性的实现

    一.什么是接口幂等性?

      一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数或幂等方法是指可以使用相同参数重复执行,并能获得相同结果的函数/方法。这些函数/方法不会影响系统状态,因此不用担心重复执行会对系统造成改变。

      个人理解,从后端的角度通俗来说就是:

        对于一个请求,在参数相同的情况下,请求一次或者请求多次,响应结果都是一致的;

    二.为什么需要接口幂等性的支持?

      1.前端重复提交数据的情况下,后端之应该返回相同的数据结果或只对该次提交产生一个反应结果;

      2.手机支付的场景下,发起支付时,如遇到网络重发或者系统BUG重发(例如网络延迟导致 Feign重试的情况)应该只扣一次款;

      3.消息推送的场景下,消息推送只发一次;

      4.用户下单创建业务订单的场景下,一次请求只能创建一个,而不能因为网络或系统问题多次创建;

    三.常见的接口幂等性设计方案(以Http协议的接口为例)

      1.查询操作(GET请求)

        由于GET请求具有天然的幂等性,因此查询一次和查询多次都是幂等的

      2.删除操作(DELETE请求)

        在参数相同的情况下,删除一次和多次删除的结果都是一致的,结果都是将该数据删除

      3.数据库唯一索引,新增数据时防止增加脏数据

        适用场景:

          例如在创建账号时,一个手机号只能创建一个账号,因此对于可能操作问题出现的短时间的重复创建问题,除了代码中的逻辑校验之外,使用数据库唯一索引是比较适用的方式,可以用来防止新增数据存在脏数据;

        优化:

          并发新增数据时,对于数据添加失败的情况下,可以捕获异常,在catch代码块中再根据该唯一索引字段查询一边数据,返回与正常数据添加的相同的结果,从而友好的提示前端数据;

      4.唯一token机制,防止页面重复提交

        业务要求:

        页面的数据只能被点击提交一次

        发生原因:由于重复点击或者网络重发,或者nginx重发等情况会导致数据被重复提交

        解决办法:集群环境:采用token加redis(redis单线程的,处理需要排队) 单JVM环境:采用token加redis或token加jvm内存

        处理流程:

        •     数据提交前要向服务的申请token,token放到redis或jvm内存,token有效时间

        •     提交后后台校验token,同时删除token,生成新的token返回

        token特点:

        要申请,一次有效性,可以限流

        注意:redis要用删除操作来判断token,删除成功代表token校验通过,如果用select+delete来校验token,存在并发问题,不建议使用

      5.悲观锁   

      获取数据的时候加锁获取

          select * from table_xxx where id='xxx' for update;

        注意:id字段一定是主键或者唯一索引(这样只锁定索引对应的那一行),不然会锁表

      悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用

      6.乐观锁  

        乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。

        乐观锁的实现方式多种多样可以通过version或者其他状态条件:

        1.通过版本号实现:(版本号需要提前返回给前端)

            update table_xxx set name=#name#,version=version+1 where version=#version#

        2.通过条件限制

            update tablexxx set avaiamount=avaiamount-#subAmount# where avaiamount-#subAmount# >= 0

        要求:quality-#subQuality# >= ,这个情景适合不用版本号,只更新是做数据安全校验,适合库存模型,扣份额和回滚份额,性能更高

        注意:乐观锁的更新操作,最好用主键或者唯一索引来更新,这样是行锁,否则更新时会锁表,上面两个sql改成下面的两个更好

           update tablexxx set name=#name#,version=version+1 where id=#id# and version=#version#update tablexxx set avaiamount=avaiamount-#subAmount# where id=#id# and avai_amount-#subAmount# >= 0

      7.对外提供接口的api如何保证幂等

        如银联提供的付款接口:需要接入商户提交付款请求时附带:source来源,seq序列号

        source+seq在数据库里面做唯一索引,防止多次付款,(并发时,只能处理一个请求)

        重点 对外提供接口为了支持幂等调用,接口有两个字段必须传,一个是来源source,一个是来源方序列号seq,这个两个字段在提供方系统里面做联合唯一索引

        这样当第三方调用时,先在本方系统里面查询一下,是否已经处理过,返回相应处理结果;没有处理过,进行相应处理,返回结果。

    注意,为了幂等友好,一定要先查询一下,是否处理过该笔业务,不查询直接插入业务系统,会报错,但实际已经处理了。

     

         

  • 相关阅读:
    Scite 中文支持
    【线段上】简单贪心总结……未完
    Happy new year!
    poj 2960 SNim
    【转】SG函数资料(入门必备)
    poj 2478 Farey Sequence
    Poj 3083 Children of the Candy Corn
    Poj 1077 Eight 八数码
    Poj 1830 开关问题:高斯消元
    关于poj 放苹果
  • 原文地址:https://www.cnblogs.com/july-sunny/p/12269306.html
Copyright © 2011-2022 走看看