美文网首页
Redis+token实现幂等性

Redis+token实现幂等性

作者: 杨健kimyeung | 来源:发表于2020-08-24 09:03 被阅读0次

一、概念

一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同

幂等:是一个数学概念,表示N次变换和1次变换的结果相同。

幂等操作:其特点是任意多次执行所产生的影响均与一次执行的影响相同(不会改变资源状态,对数据没有副作用)。

幂等性:一系列操作都是幂等操作。

幂等接口:幂等接口认为,外部调用者会存在多次调用的场景,为了防止重试对数据状态的改变,需要将接口的设计为幂等的

二、幂等性和重复提交的区别

  • 重复提交是在第一次请求已经成功的情况下,导致不满足幂等要求的服务多次改变状态。

  • 幂等更多使用的情况是第一次请求不知道结果(比如超时)或者失败的异常情况下,发起多次请求,目的是多次确认第一次请求成功,却不会因多次请求而出现多次的状态变化。

三、幂等场景

  1. 重试机制:微服务场景,在我们传统应用架构中调用接口由于网络原因可能会超时。如果超时了,微服务框架会进行重试。

  2. 用户重复操作:客服端多次点击。如:快速点击按钮多次。

  3. MQ消息中间件:消息重复消费

  4. 调用第三方平台的接口,因为异常也会导致多次异步回调

四、不足

  1. 增加了额外控制幂等的业务逻辑,复杂化了业务功能;

  2. 把并行执行的功能改为串行执行,降低了执行效率。

五、设计幂等性分析

5.1、数据库层面

增加(C)

数据库自增主键,不具备幂等性

查询(R)

重复查询不会产生或变更新的数据,因此查询是天然具备幂等性

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n35" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; color: rgb(184, 191, 198); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">select * from goods where xxx</pre>

更新(U)

  • 基于主键的计算赋值,不具备幂等性,

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="sql" cid="n40" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit;">UPDATE goods SET number=number-1 WHERE id=1`</pre>

  • 基于主键的非计算式,具备幂等性,

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="sql" cid="n43" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit;">UPDATE goods SET number=newNumber WHERE id=1</pre>

  • 基于条件查询,不一定具有幂等性(需要根据实际情况进行分析判断)

删除(D)

  • 基于主建的delete具备幂等性

  • 一般业务层面都是逻辑删除(即update操作),而基于主键的逻辑删除操作也是具有幂等性的

5.2、接口层面(HTTP)

在HTTP规范中定义GET,PUT和DELETE方法应该具有幂等性。

POST方法

HTTP POST 方法是一个非幂等方法,因为调用多次,都将产生新的资源。因为它会对资源本身产生影响,每次调用都会有新的资源产生,因此不具备幂等性

GET方法

GET方法是向服务器查询,不会对系统产生副作用,具有幂等性(不代表每次请求都是相同的结果)

PUT方法

也就是说PUT方法首先判断系统中是否有相关的记录,如果有记录则更新该记录,如果没有则新增记录。

DELETE 方法

DELETE方法是删除服务器上的相关记录。

六、解决幂等性方案

唯一索引

这是最容易想到的方式。页面的数据通常只能被提交一次,多次提交可能会产生脏数据。比如,同一名称的商品只能被创建一次,为了防止创建多次,可以给商品名称添加唯一索引。当在添加一个已有名称的商品时,数据库插入操作就会因为唯一索引而引发异常,避免了脏数据的产生。类似的案例还有博客点赞,订单创建等场景。

唯一索引不仅可以解决并发下的脏数据问题,也可以解决永久性幂等的问题。

缺点: 无法适用分布式存储系统,需要维护数据库的唯一索引,多的情况下不容易管理

分布式锁

如果是分布式系统,全局的唯一索引就很难构建,此时可以使用分布式锁的方式解决此类问题。我们可以在执行第操作时先获取分布式锁,做完操作后,再将分布式锁释放。这样可以解决高并发性下的幂等性问题。

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n70" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; color: rgb(184, 191, 198); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@Controller
String addOrder(order){
redisLock.lock();
addOrder(order);
redisLock.unLock();
}</pre>

需要配合 select+insert 使用,如果不配合,无法解决表单重复提交的问题。

MVCC 机制

MVCC(Multi-Version Concurrency Control) 多版本并发控制。在数据更新时需要去比较持有数据的版本号,版本号不一致的操作无法进行更新,更新成功后版本号将会发生变化。

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n75" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; color: rgb(184, 191, 198); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> updateStock(int num, int version) {
if (version != currentVersion) {
throw Exception;
} else {
stockDao.minStock();
}
}</pre>

注意,只适用于更新接口

状态机幂等

所谓状态机,就是任务或者业务在执行的过程中,拥有的状态以及状态的变更图。在执行某个操作前,需要先对当前状态进行验证,如果状态不是该有的状态,则拒绝操作。

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n80" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; color: rgb(184, 191, 198); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> String pay() {
if (order.status == "待支付") {
doPay();
}
}</pre>

后台管理员在新增数据时,由于特殊原因,比如按钮抖动,而导致目标数据插入两条;用户下单时,没有做逻辑校验,导致用户下了多笔相同的订单。这些情况都是不被允许的,我们要保证这些操作无论执行多少次,最终产生的结果都是相同的,这类业务通常需要拥有较强的一致性。幂等性就是描述这一接口特性的名词。

参考

深入理解幂等性

分布式防重复提交token设计

幂等性的总结

相关文章

  • Redis+token实现幂等性

    一、概念 一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对...

  • SpringBoot接口幂等性实现的4种方案!

    目录 什么是幂等性 什么是接口幂等性 为什么需要实现幂等性 引入幂等性后对系统的影响 Restful API 接口...

  • 什么是接口的幂等性,如何实现接口幂等性?一文搞定

    每天一个知识点 什么是接口的幂等性,如何实现接口幂等性? (一)幂等性概念 幂等性原本是数学上的概念,用在接口上就...

  • 幂等性

    理解 HTTP 幂等性(15')系统幂等以及常用实现方式(10')分布式系统互斥性与幂等性问题的分析与解决(30')

  • 编程思想之幂等性

    前言 今天面试被问到幂等性如何实现,答的稀里糊涂,回来冷静的思考一下 什么是幂等性 既然幂等性源于数学,那我就使用...

  • 接口幂等性

    接口幂等性 什么是接口幂等性? 最简单想到的实现接口幂等性(重复提交)的操作是什么? 最简单稍靠谱的解决方案是什么?

  • 幂等性实现方案

    一、什么是幂等性 所谓幂等性,简单地说,就是对接口的多次调用所产生的结果和调用一次是一致的。扩展一下,这里的接口...

  • 幂等性实现方案

    幂等性实现方案 字面理解 幂 名词:盖东西的巾大巾谓之幂。——《小尔雅·广诂》 动词:覆盖,遮盖祭祀,以疏布巾幂八...

  • 幂等

    再谈幂等机制 一、什么是幂等性? 幂等性(Idempotence)。在HTTP/1.1规范中幂等性的定义是: Me...

  • SpringCloud 幂等性分布式解决方案

    幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数 研究了很久的幂等性解决方案。终于实现了...

网友评论

      本文标题:Redis+token实现幂等性

      本文链接:https://www.haomeiwen.com/subject/jotvjktx.html