常见接口和服务幂等性问题及解决方案

简介: 常见接口和服务幂等性问题及解决方案

1 幂等性介绍

很多时候大家接触的这个词常在消息队列听到,如何解决MQ幂等性也是面试的一个重点,但是你正着理解这个词吗?比如现如今很多系统都会基于分布式或微服务思想完成对系统的架构设计。那么在这一个系统中,就会存在若干个微服务,而且服务间也会产生相互通信调用。那么既然产生了服务调用,就必然会存在服务调用延迟或失败的问题。当出现这种问题,服务端会进行重试等操作或客户端有可能会进行多次点击提交。如果这样请求多次的话,那最终处理的数据结果就一定要保证统一,如支付场景。此时就需要通过保证业务幂等性方案来完成。


举个例子:支付,用户购买商品后支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额发现多扣钱了,流水记录也变成了两条。在以前的单应用系统中,我们只需要把数据操作放入事务中即可,发生错误立即回滚,但是再响应客户端的时候也有可能出现网络中断或者异常等等。


在增删改查4个操作中,尤为注意就是增加或者修改,查询对于结果是不会有改变的,删除只会进行一次,用户多次点击产生的结果一样,修改在大多场景下结果一样,增加在重复提交的场景下会出现。


fd06e701e48747ae8e87571a4e00883f.png


2 restful风格调用产生的问题

GET:用于获取资源,多次操作不会对数据产生影响,具有幂等性。注意不是结果。


POST:用于新增资源,对同一个URI进行两次POST操作会在服务端创建两个资源,不具有幂等性。


PUT:用于修改资源,对同一个URI进行多次PUT操作,产生的影响和第一次相同,具备幂等性。


DELETE:用于删除资源,对同一个URI进行多次DELETE操作,产生的影响和第一次相同,具备幂等性。


综上所述,这些仅仅只是HTTP协议建议在基于RESTFUL风格定义WEB API时的语义,并非强制性。同时对于幂等性的实现,肯定是通过前端或服务端完成。


3 接口幂等性问题解决

对于幂等的考虑,主要解决两点前后端交互与服务间交互。这两点有时都要考虑幂等性的实现。从前端的思路解决的话,主要有三种:前端防重、PRG模式、Token机制。


3.1 前端防重

通过前端防重保证幂等是最简单的实现方式,前端相关属性和JS代码即可完成设置。可靠性并不好,有经验的人员可以通过工具跳过页面仍能重复提交。主要适用于表单重复提交或按钮重复点击。


3.2 PRG模式防止表单重复提交

PRG模式即POST-REDIRECT-GET。当用户进行表单提交时,会重定向到另外一个提交成功页面,而不是停留在原先的表单页面。这样就避免了用户刷新导致重复提交。同时防止了通过浏览器按钮前进/后退导致表单重复提交。


网络延迟时,重复点击提交按钮,有可能发生重复提交表单问题。


解决方案:


1.数据库主键唯一。

2.提交成功后重定向。

3.使用 JavaScript 解决,使用标记位,提交后隐藏或不可用提交按钮。 使用


Session 解决: 生成唯一的 Token 给客户端,客户端第一次提交时带着这个 Token,后台与 Session 中的 进行对比。一样则提交成功并清除 Session 中的 Token。不一样则提交失败。


4 常用Token机制解决服务幂等性

基于以下过程图,实现的话很简单就不进行代码的编写了


4.1 方案介绍

通过token机制来保证幂等是一种非常常见的解决方案,同时也适合绝大部分场景。该方案需要前后端进行一定程度的交互来完成。



6a47a2cc1ab04bfab6ad38268489d2c3.png

以上的图片现在有一个问题,当前是先执行业务再删除token。在高并发下,很有可能出现第一次访问时token存在,完成具体业务操作。但在还没有删除token时,客户端又携带token发起请求,此时,因为token还存在,第二次请求也会验证通过,执行具体业务操作。对于这个问题的解决方案的思想就是并行变串行。会造成一定性能损耗与吞吐量降低。


第一种方案:对于业务代码执行和删除token整体加线程锁。当后续线程再来访问时,则阻塞排队。这样效率不行


第二种方案:借助redis单线程和incr(自增)是原子性的特点。当第一次获取token时,以token作为key,对其进行自增。然后将token进行返回,当客户端携带token访问执行业务代码时,对于判断token是否存在不用删除,而是对其继 续incr。如果incr后的返回值为2。则是一个合法请求允许执行,如果是其他值,则代表是非法请求,直接返回。


918614273ce14ca78f3c81d468ea9384.png


那如果先删除token再执行业务呢?其实也会存在问题,假设具体业务代码执行超时或失败,没有向客户端返回明确结果,那客户端就很有可能会进行重试,但此时之前的token已经被删除了,则会被认为是重复请求,不再进 行业务处理。


3b5a7b11601f49b09c278afd7d10899a.png

这种方案无需进行额外处理,一个token只能代表一次请求。一旦业务执行出现异常,则让客户端重新获取令牌,重新发起一次访问即可。


推荐使用先删除token方案但是无论先删token还是后删token,都会有一个相同的问题。每次业务请求都回产生一个额外的请求去获取token。但是,业务失败或超时,在生产环境下,一万个里最多也就十个左右会失败,那为了这十来个请求,让其他九千九百多个请求都产生额外请求,就有一些得不偿失了。虽然redis性能好,但是这也是一种资源的浪费。


5 服务幂等性

其实我也不想套娃,发现现在写的东西都以前写过,只不过就是换一个名词而已。有时候面试的时候会问道,你们是怎么解决接口幂等性,服务幂等性。其实换种说法就是是否产生重复数据,是否对现有数据产生影响,是并发高的情况下还是并发一般的情况下产生的,如何去解决。

5.1 并发不高和高并发的情况加锁解决

举个例子服务幂等了,就是超时的一个重试机制造成的重复扣库存

解决方案:Redis高并发场景下秒杀超卖解决



目录
相关文章
|
SQL 缓存 NoSQL
接口的幂等性设计和防重保证,详细分析幂等性的几种实现方法
本篇文章详细说明了幂等性,解释了什么是幂等性,幂等性的使用场景,讨论了幂等和防重的概念。分析了幂等性的情况以及如何设计幂等性服务。阐述了幂等性实现防重的几种策略,包括乐关锁,防重表,分布式锁,token令牌以及支付缓冲区。
9804 0
接口的幂等性设计和防重保证,详细分析幂等性的几种实现方法
|
NoSQL Java Redis
SpringBoot集成Redis解决表单重复提交接口幂等(亲测可用)
SpringBoot集成Redis解决表单重复提交接口幂等(亲测可用)
1078 0
|
存储 缓存 安全
接口的幂等性
接口的幂等性
438 0
|
Java 数据库
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
设计模式 缓存 前端开发
什么是幂等性?四种接口幂等性方案详解!
本文深入分布式系统中的幂等性问题及其解决方案,涵盖数据库唯一主键、乐观锁、PRG模式和防重Token等方法,关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
什么是幂等性?四种接口幂等性方案详解!
|
关系型数据库 数据库 索引
间隙锁
间隙锁是数据库中用于解决幻读问题的锁机制,主要在可重复读隔离级别下生效。它通过锁定索引记录之间的间隙,阻止其他事务插入新数据,从而保证查询结果的一致性。间隙锁在范围查询、唯一索引不存在记录时等场景触发,既能防止并发插入冲突,也可能降低并发性能并引发死锁。合理配置隔离级别和优化查询条件可减少其影响。
575 0
|
JSON JavaScript 前端开发
JavaScript实现字符串转json对象的方法
JavaScript实现字符串转json对象的方法
|
设计模式 XML SQL
【SpringFramework】面向切面编程-SpringAOP
本文简要记录了Spring AOP相关知识点,及基本的使用方法。
345 14
【SpringFramework】面向切面编程-SpringAOP
|
消息中间件 存储 Kafka
MQ保证消息幂等机制
MQ保证消息幂等机制
695 0
|
存储 缓存 安全
如何保证接口幂等性,幂等性到底是干什么的
本文介绍了幂等性原则及其在程序中的应用。首先定义了幂等性,即无论执行多少次,结果不变的特性,并区分了幂等与非幂等操作。接着详细探讨了实现幂等性的策略,如使用唯一标识符、幂等性标记字段、乐观锁版本控制等。最后,通过Java示例展示了如何实现无状态幂等操作,并强调了幂等性在分布式系统和高并发场景下的重要性。
1594 0