java|阿里面试官(如何测试接口幂等性())

一.什么是接口幂等性

  • 同一个接口,多次发出同一个请求,必须保证操作只执行一次
  • 调用接口发生异常并且重复尝试时,总是会造成系统所无法承受的损失,所以必须阻止这种现象的发生。
  • 比如支付接口,重复支付会导致多次扣钱;订单接口,同一个订单可能会多次创建
二.什么情况下会出现接口幂等性问题?
  • 网络波动,可能会引起重复请求
  • 用户重复操作,用户在操作时候可能会无意触发多次下单交易
  • 使用了失效或超时重试机制
  • 页面重复刷新
  • 使用浏览器后退按钮重复之前的操作,导致重复提交表单
  • 使用浏览器历史记录重复提交表单
  • 浏览器重复的HTTP请求
  • 定时任务重复执行
三.如何保证接口幂等性?
  • 按钮只操作一次
    一般是提交后把按钮置灰或loading状态,消除用户因为重复点击而产生的重复记录,比如添加操作,由于点击两次而产生两条记录
  • token机制
    【java|阿里面试官(如何测试接口幂等性())】功能上允许重复提交,但要保证重复提交不产生副作用
    比如点击n次只能产生一条记录,具体实现就是进入页面时申请一个token,然后后面所有的请求都带上这个token,后端根据token来避免重复请求
    具体步骤:
    • 用户访问页面时,浏览器自动发起获取token请求。
    • 服务端生成token,保存到redis中,然后返回给浏览器。
    • 用户通过浏览器发起请求时,携带该token。
    • 在redis中查询该token是否存在,如果不存在,说明是第一次请求,做则后续的数据操作。
    • 如果存在,说明是重复请求,则直接返回成功。
    • 在redis中token会在过期时间之后,被自动删除。
  • 使用Post/Redirect/Get模式
    在提交后执行页面重定向
    当用户提交连表单后,跳转到一个重定向的信息页面
    避免用户按F5刷新导致的重复提交,而且也不会出现浏览器表单重复提交的警告,也能消除按浏览器前进和后退导致同样重复提交的问题。
  • 在session存放特殊标志
    在服务端生成一个唯一的标识符,将它存入session
    同时前端获取这个标识符的值将它写入表单的隐藏中,用于用户输入信息后点击一起提交
    在服务端,获取表单中隐藏字段的值,与session中的唯一标识符比较,相等说明时首次提交,就将处理本次请求,然后将session中的唯一标识符移除,不相等则表示是重复提交,不再做处理。
  • 使用唯一索引放置新增脏数据
    利用数据库唯一索引机制,当数据重复时,插入数据库会抛出异常,保证不会出现脏数据
java|阿里面试官(如何测试接口幂等性())
文章图片

  • 具体步骤:
    用户通过浏览器发起请求,服务端收集数据。
    将该数据插入mysql
    判断是否执行成功,如果成功,则操作其他数据(可能还有其他的业务逻辑)。
    如果执行失败,捕获唯一索引冲突异常,直接返回成功。
  • 乐观锁
    如果更新已有数据,可以进行加锁更新,也可以设计表结构时使用乐观锁,通过version来做乐观锁,这样既能保证执行效率,又能保证幂等。
java|阿里面试官(如何测试接口幂等性())
文章图片

具体步骤
  • 先根据id查询用户信息,包含version字段
  • 根据id和version字段值作为where条件的参数,更新用户信息,同时version+1
  • 判断操作影响行数,如果影响1行,则说明是一次请求,可以做其他数据操作。
  • 如果影响0行,说明是重复请求,则直接返回成功。
  • 分布式锁
    通过第三方系统(redis或zookeeper),在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁
    3种实现redis的分布式锁
    • setNx命令
    • set命令
    • Redission框架
java|阿里面试官(如何测试接口幂等性())
文章图片

  • 具体步骤:
    用户通过浏览器发起请求,服务端会收集数据,并且生成订单号code作为唯一业务字段。
    使用redis的set命令,将该订单code设置到redis中,同时设置超时时间。
    判断是否设置成功,如果设置成功,说明是第一次请求,则进行数据操作。
    如果设置失败,说明是重复请求,则直接返回成功。
  • 状态机幂等
    在设计单据相关业务,或者任务相关的业务,会涉及到状态机
    业务单据上面有个状态,状态在不同的情况下会发生变更,一般情况下存在有限状态机,这时候,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。
    java|阿里面试官(如何测试接口幂等性())
    文章图片

具体步骤:
  • 用户通过浏览器发起请求,服务端收集数据。
  • 根据id和当前状态作为条件,更新成下一个状态
  • 判断操作影响行数,如果影响了1行,说明当前操作成功,可以进行其他数据操作。
  • 如果影响了0行,说明是重复请求,直接返回成功。
该方案仅限于要更新的表有状态字段,并且刚好要更新状态字段的这种特殊情况,并非所有场景都适用。
  • 防重表
    以支付为例:使用唯一主键去做防重表的唯一索引,比如使用订单号作为防重表的唯一索引,每一次请求都根据订单号向防重表中插入一条数据,插入成功说明可以处理后面的业务,当处理完业务逻辑之后删除防重表中的订单号数据,后续如果有重复请求,则会因为防重表唯一索引原因导致插入失败,直接返回操作失败,直到第一请求返回结果,可以看出防重表作用就是加锁的功能。
java|阿里面试官(如何测试接口幂等性())
文章图片

具体步骤:
  • 用户通过浏览器发起请求,服务端收集数据。
  • 将该数据插入mysql防重表
  • 判断是否执行成功,如果成功,则做mysql其他的数据操作(可能还有其他的业务逻辑)。
  • 如果执行失败,捕获唯一索引冲突异常,直接返回成功。
需要特别注意的点:防重表和业务表必须在同一个数据库中,并且操作要在同一个事务中。
缓冲队列
将请求都快速地接受下来后放入缓冲队列中,后续使用异步任务处理队列中的数据,过滤掉重复的请求。
优点:同步处理改成异步处理、高吞吐量。
缺点:不能及时返回请求结果,需要后续轮询得处理结果。
全局唯一ID
通过source来源+唯一序列号传入给后端,后端来判断请求是否重复,在并发时只能处理一个请求,其他相同并发请求要么返回请求重复,要么等待前面请求执行完后再执行。
绵薄之力 最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走
java|阿里面试官(如何测试接口幂等性())
文章图片

这些资料,对于想进阶【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。希望对大家有所帮助,需要的小伙伴点击下方插件进群免费领取:

    推荐阅读