乐趣区

关于后端:如何保证接口幂等性一口气说了12种方法

本文曾经收录到 Github 仓库,该仓库蕴含 计算机根底、Java 根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构 等外围知识点,欢送 star~

Github 地址:https://github.com/Tyson0314/…

Gitee 地址:https://gitee.com/tysondai/Ja…

大家好,我是大彬~

明天来聊聊接口幂等性。

什么是接口幂等性?如何保障接口幂等性?

什么是接口幂等性?

首先看看幂等性的概念:

幂等性本来是数学上的概念,用在接口上就能够了解为:同一个接口,屡次收回同一个申请,必须保障操作只执行一次。调用接口产生异样并且反复尝试时,总是会造成零碎所无奈接受的损失,所以必须阻止这种景象的产生。

比方上面这些状况,如果没有实现接口幂等性会有很重大的结果:领取接口,反复领取会导致屡次扣钱;订单接口,同一个订单可能会屡次创立。

为什么会产生接口幂等性问题?

那么,什么状况下,会产生接口幂等性的问题呢?

  • 网络稳定, 可能会引起反复申请
  • 用户反复操作, 用户在操作时候可能会无心触发屡次下单交易, 甚至没有响应而无意触发屡次交易利用
  • 应用了生效或超时重试机制(Nginx 重试、RPC 重试或业务层重试等)
  • 页面反复刷新
  • 应用浏览器后退按钮反复之前的操作, 导致反复提交表单
  • 应用浏览器历史记录反复提交表单
  • 浏览器反复的 HTTP 申请
  • 定时工作反复执行
  • 用户双击提交按钮

如何保障接口幂等性?

那么最要害的来了,如何保障接口幂等性?

解决办法分为两个方向,一个方向是客户端避免反复调用,一个是服务端进行校验。当然,客户端避免反复提交并不是相对牢靠的,长处是实现起来比较简单。

按钮只可操作一次

个别是提交后把按钮置灰或 loding 状态, 打消用户因为反复点击而产生的重复记录, 比方增加操作, 因为点击两次而产生两条记录

token 机制

性能上容许反复提交, 但要保障反复提交不产生副作用, 比方点击 n 次只产生一条记录, 具体实现就是进入页面时申请一个 token, 而后前面所有的申请都带上这个 token, 后端依据 token 来防止反复申请。

应用 Post/Redirect/Get 模式

在提交后执行页面重定向, 这就是所谓的 Post-Redirect—Get(PRG)模式, 简略来说就是当用户提交连表单后, 跳转到一个重定向的信息页面, 这样就防止用户按 F5 刷新导致的反复提交, 而且也不会呈现浏览器表单反复提交的正告, 也能打消按浏览器后退和后退导致同样反复提交的问题。

在 session 寄存非凡标记

在服务端, 生成一个惟一的标识符, 将它存入 session, 同时前端获取这个标识符的值将它写入表单的暗藏中, 用于用户输出信息后点击一起提交, 在服务器端, 获取表单中暗藏字段的值, 与 session 中的惟一标识符比拟, 相等阐明是首次提交, 就解决本次申请, 而后将 session 中的惟一标识符移除, 不相等则示意是反复提交, 不再做解决。

应用惟一索引避免新增脏数据

利用数据库惟一索引机制, 当数据反复时, 插入数据库会抛出异样, 保障不会呈现脏数据。

乐观锁

如果更新已有数据, 能够进行加锁更新, 也能够设计表构造时应用乐观锁, 通过 version 来做乐观锁, 这样既能保障执行效率, 又能保障幂等, 乐观锁的 version 版本在更新业务数据要自增
update table set version = version + 1 where id = #{id} and version = #{version}
示例: 当有反复申请的时候, 第一个申请会获取以后商品的 version 版本号, 失去的 version 为 1, 紧接着因为第一个申请还没更新商品的 version, 第二个申请获取的 version 仍然也是 1, 这时候第一个申请操作更新的时候带上 version 并作为条件并且自增更新, 这时候商品的 version 就会变成 2, 当第二个申请去操作更新的时候显著 version 不统一导致更新失败。

select + insert or update or delete

该计划就是操作之前先查问一下, 符合要求再插入, 该计划在没有并发的零碎中能够解决幂等问题,在单 JVM 有并发的时候能够用 JVM 加锁来保障幂等性, 在分布式环境它是无奈保障幂等性, 能够应用分布式来保障。

分布式锁

如果是分布式系统,构建全局惟一索引比拟艰难,例如唯一性的字段没法确定,这时候能够引入分布式锁,通过第三方的零碎 (redis 或 zookeeper),在业务零碎插入数据或者更新数据,获取分布式锁,而后做操作,之后开释锁。要点:某个长流程处理过程要求不能并发执行,能够在流程执行之前依据某个标记(用户 ID+ 后缀等) 获取分布式锁,其余流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行胜利,执行实现后,开释分布式锁(分布式锁要第三方零碎提供)。

状态机幂等

在设计单据相干的业务,或者是工作相干的业务,必定会波及到状态机(状态变更图),就是业务单据下面有个状态,状态在不同的状况下会产生变更,个别状况下存在无限状态机,这时候,如果状态机曾经处于下一个状态,这时候来了一个上一个状态的变更,实践上是不可能变更的,这样的话,保障了无限状态机的幂等。留神:订单等单据类业务,存在很长的状态流转,肯定要深刻理解状态机,对业务零碎设计能力进步有很大帮忙。

防重表

以领取为例: 应用惟一主键去做防重表的惟一索引, 比方应用订单号作为防重表的惟一索引, 每一次申请都依据订单号向防重表中插入一条数据, 插入胜利阐明能够解决前面的业务, 当解决完业务逻辑之后删除防重表中的订单号数据, 后续如果有反复申请, 则会因为防重表惟一索引起因导致插入失败, 间接返回操作失败, 直到第一次申请返回后果, 能够看出防重表作用就是加锁的性能。
注: 最好联合状态机幂等先判断一下

缓冲队列

将申请都疾速地接管下来后放入缓冲队列中, 后续应用异步工作解决队列中的数据, 过滤掉反复的申请, 该解决方案长处是同步解决改成异步解决、高吞吐量, 毛病则是不能及时地返回申请后果, 须要后续轮询得处理结果。

全局惟一号

比方通过 source 起源 + 惟一序列号传入给后端,后端来判断申请是否反复, 在并发时只能解决一个申请, 其余雷同并发申请要么返回申请反复, 要么期待后面申请执行实现后再执行。

最初给大家分享一个 Github 仓库,下面有大彬整顿的 300 多本经典的计算机书籍 PDF,包含 C 语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生 等,能够 star 一下,下次找书间接在下面搜寻,仓库继续更新中~

Github 地址:https://github.com/Tyson0314/…

退出移动版