接口幂等性解决方案

article/2025/10/3 17:20:03

一、分布式锁解决方案

先说这种方案,在网上有一些文章说可以通过分布式锁来保证幂等性。但是我认为这种方案不能保证幂等性,不可取。看下面分析

①、方案流程介绍

  1. 用户通过浏览器发起请求,服务端接收请求数据,并且从请求数据中获取订单号code作为唯一业务字段。
  2. 使用redis的set命令,将该订单code设置到redis中,同时设置超时时间。
  3. 判断是否设置成功,如果设置成功,说明是请求没有处理过。
  4. 数据操作完成后释放锁
  5. 如果设置失败,说明请求请求已经处理过,则直接返回成功(如果需要返回业务数据,那么这里还需要查询出业务数据然后返回)。

②、问题分析

case1、客户端连续发起两次请求(比如用户快速点击按钮的情况),第一次请求先到达服务端,然后第二次请求由于某些原因过了一会儿才到达服务端。等第二次请求达到服务端的时候,第一次请求已经执行完毕并且释放了锁。此时第二次请求仍然能加锁成功,并且执行业务逻辑。这种情况下幂等性失效。

case2、客户端发起第一次请求,服务端正常执行完毕并释放了分布式锁,但由于网络原因客户端没有正常收到服务端的响应,此时客户端再次发起请求。由于第一次请求所加的分布式锁已经过期所以第二次请求仍然能够加锁成功,让后执行业务逻辑。此时幂等性失效。

case3、客户端连续发起多次请求,这多次请求同时到达服务端,此时开始争抢锁,谁抢到锁谁就执行,其他没有抢到锁的请求都统统不执行。这种情况能保证幂等性。

所以这种方案不能保证请求幂等。

二、乐观锁解决方案

这种基于乐观锁的方式局限性太大了,并且该方案应该叫做防止重复提交解决方案,称为幂等解决方案不是那么贴切,适用面比较狭窄!

①、适用操作

  • 简单更新操作

②、方案介绍

数据库乐观锁方案一般只能适用于执行更新操作的过程,我们可以提前在对应的数据表中多添加一个字段,充当当前数据的版本标识。这样每次对该数据库该表的这条数据执行更新时,都会将该版本标识作为一个条件,值为上次待更新数据中的版本标识的值。

  • 客户端查询后端接口数据,此时返回的数据中包含版本号(比如版本号为 x)
  • 客户端提交请求(带上之前查询时得到的版本号)
  • 接口接收到请求后,按照提交的版本号去更新数据
  • 如果更新失败则说明是重复请求,直接异常中断或者查询出上次执行的结果数据返回即可

③、优缺点

  • 实现简单,只需要在表中增加一个字段即可。

  • 适用面太狭窄,并且只能针对于极其简单的业务逻辑。并且版本号需要外部传入,不安全。

三、数据库唯一key解决方案

①、适用操作

  • 插入操作

②、方案介绍

  • 在表设计的时候我们可以规定一些从业务上唯一的字段(比如:身份证号、分布式主键ID),为这些字段建立一个唯一索引。

  • 在接口做插入操作的时候,第一次请求数据可以插入成功。但后面的相同请求,插入数据时会报Duplicate entry 'xxx' for key 'xxxxxxx异常,表示唯一索引有冲突。

  • 虽说抛异常对数据来说没有影响,不会造成错误数据。但是为了保证接口幂等性,我们需要对该异常进行捕获,然后返回成功。

    如果是java程序需要捕获:DuplicateKeyException异常,如果使用了spring框架还需要捕获:MySQLIntegrityConstraintViolationException异常。

具体步骤

  1. 用户通过浏览器发起请求,服务端接收数据。
  2. 将该数据插入mysql
  3. 判断是否执行成功,如果成功,则操作其他数据(可能还有其他的业务逻辑)。
  4. 如果执行失败,捕获唯一索引冲突异常,直接返回成功。

③、优缺点分析

  • 使用起来比较简单,只需要确定好哪个是唯一key,然后建立唯一索引即可
  • 编码上比较麻烦,因为每个需要保证幂等的插入类型的接口都需要去做捕获DuplicateKeyException异常的操作,代码上比较冗余。
  • 适用面不广,只能适用于插入操作,并且只能适用于简单的业务场景,对于稍微复杂的业务场景便不太适用了
  • 效率不高,基于数据库的唯一key去做防重和保证插入幂等,那么相当于把压力放到了数据库上。在高并发的情况下很可能出现性能问题。

四、Redis + token解决方案

①、适用操作

  • 更新操作
  • 新增操作
  • 这种方案比较适合于前端界面和后端接口交互的一种幂等方式
  • 这种方案由于不依赖于接口内部代码进行判断,所以可以通过拦截器或AOP切面 + 注解的方式做的更加通用,仅用一个注解就能让某个接口保证幂等性。

②、方案介绍

  1. 服务端需要提供一个token获取接口(该 Token 可以是一个序列号,也可以是一个分布式 ID 或者 UUID 串,反正要保证唯一性),客户端调用接口获取 Token,这时候服务端会生成一个 Token 串。
  2. 然后将该串存入 Redis 数据库中,以该 Token 作为 Redis 的键(注意设置过期时间)。
  3. 将 Token 返回到客户端,客户端拿到后应存到表单隐藏域中。
  4. 客户端在执行提交表单时,把 Token 存入到 Headers 中,执行业务请求带上该 Headers
  5. 服务端接收到请求后从 Headers 中拿到 Token,然后根据 Token 到 Redis 中查找该 key 是否存在。
  6. 服务端根据 Redis 中是否存该 key 进行判断,如果存在就将该 key 删除,然后正常执行业务逻辑。如果不存在就抛异常,返回重复提交的错误信息。

③、问题分析

  • 这种方案不需要在业务代码里做幂等校验,通过AOP切面 + 注解可以做的非常通用,使用起来很方便。但需要前端多发一次请求去请求token
  • 如果客户端连续发起调用,只要每次使用的token是一样的,那么这些连续的请求只会被处理一次。由此可以正常保证幂等性。
  • 如果某个客户端第一次发起请求,然后服务端收到后将token从Redis中删除,接着去执行业务逻辑,但是业务逻辑执行失败了,此时有两种可能:
    • 此时服务端可能会向客户端返回执行失败,客户端收到该返回后自动重新请求一个token,然后再次发起请求重试。这样也没有任何问题。
    • 如果此时服务端向客户端返回执行失败的过程中,由于网络或其他什么原因导致客户端无法接收到服务端返回的执行失败响应。那么此时客户端会再次使用第一次申请的token再次向服务端发送请求,但是此时服务端返回的确却是重复请求执行成功
  • 总的来说这种方案实用性较强,没有明显的缺陷。

一般来说没有任何一种幂等方案可以适用于所有场景,我们需要按照我们的实际情况来选择合适的方案即可。我们也可以采用多种方案组合使用来保证幂等性(我们可以使用token方案 + 数据库唯一key方案组合使用)。

五、通用幂等表方案

  • 该方案是非常稳定并且简单和通用的幂等解决方案!!!推荐使用
  • 基于幂等表的通用幂等组件实现,详情见:https://gitee.com/mr_wenpan/basis-enhance/tree/master/enhance-boot-idempotent

六、其他问题

1、如果是上下游接口调用如何保证幂等性呢?

比如:在上游系统调用下游系统的接口向下游接口传输数据,上游系统一般都会采用重试机制,重试调用下游接口。那么下游系统如何保证幂等性?

a)、对于新增操作

  • 我们可以考虑使用数据库的唯一key方案来实现

b)、对于修改操作

  • 对于update tbl set age = 20这种类型的场景我们不需要考虑幂等。
  • 对于update tbl set age = age + 1类型的场景我们需要考虑幂等。

c)、通用解决方案

  • 对于这种情况我们可以让上游系统每次调用接口时都传输一个唯一的key。
  • 我们下游系统接收到数据以后,先不要直接操作数据库,而是先拿着这个key,通过setNX命令设置到Redis并设置一定的过期时间(比如:一个月)
    • 如果设置成功表示第一次请求,可以正常执行后面的业务逻辑。
    • 如果设置失败,那么证明这是重复的请求,返回给上游系统重复调用的提示 或者 查询组装上次返回的数据给上游系统。
{"requestId":"3824abcxxddftb9010","data":[{"name":"zhangsan","age":21},{"name":"zhangsan","age":21}]
}

💅 注意:方案二中Redis中的key的过期时间的设置需要仔细考量!!!


http://chatgpt.dhexx.cn/article/qcnZKgdC.shtml

相关文章

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

微信搜索《Java鱼仔》,每天一个知识点不错过 每天一个知识点 什么是接口的幂等性,如何实现接口幂等性? (一)幂等性概念 幂等性原本是数学上的概念,用在接口上就可以理解为:同一个接口&#x…

什么是幂等性?四种接口幂等性方案详解

幂等性在我们的工作中无处不在,无论是支付场景还是下订单等核心场景都会涉及,也是分布式系统最常遇见的问题,除此之外,也是大厂面试的重灾区。 知道了幂等性的重要性,下面我就详细介绍幂等性以及具体的解决方案&#…

接口的幂等性

1、接口调用存在的问题 现如今我们的系统大多拆分为分布式 SOA,或者微服务,一套系统中包含了多个子系统服务,而一个子系统服务往往会去调用另一个服务,而服务调用服务无非就是使用 RPC 通信或者 restful。既然是通信,…

如何保证接口的幂等性?

什么是幂等性?所谓幂等,就是任意多次执行所产生的影响均与一次执行的影响相同。 为什么会产生接口幂等性问题 在计算机应用中,可能遇到网络抖动,临时故障,或者服务调用失败,尤其是分布式系统中,接口调用…

什么是接口的幂等性以及如何实现接口幂等性

目录 1、接口调用存在的问题 2、什么是接口的幂等性 3、不做接口的幂等性会产生什么影响 4、什么情况下需要保证接口的幂等性 4.1 select:查询操作 4.2 insert:新增操作 4.3 delete:删除操作 4.3.1 绝对删除 具有幂等性 4.3.2 相对删…

Python安装pygame教程

Python安装pygame教程 1、版本说明 由于python3.8与pygame存在不兼容的问题,因此在下载Python的时候,需要下载python3.8以下版本的,我下载的是python3.7.5 2、具体步骤 1、在本机控制台通过命令安装pygame pip install pygame 出现错误 …

Pygame 教程(2):重要的概念及对象

本章,将介绍 Pygame 中的重要概念及对象。 导航 上一章:创建第一个应用程序 下一章:绘制图形 文章目录 导航像素坐标Rect 对象Color 对象Surface 对象实例:矩形生成器创建初始窗口创建生成矩形的函数调用函数完整代码 结语 像素…

Pygame教程系列二:MoviePy视频播放篇

【前言】 在pygame 2.0.0版本之前,播放视频可以使用pygame.movie.Movie(xxxx.mpg)播放(只支持.mpg格式的视频),但是在pygame 2.0.0之后,作者因为觉得视频模块维护成本太高就给抛弃了,假如你使用pygame 2.0.0,还调用上述…

Pygame 教程(4):图像传输和绘制文本

本章,你将学会如何传输图像和绘制文本。 导航 上一章:绘制图形 下一章:监测游戏时间 文章目录 导航加载图像导出图像绘制文本实例:画板添加常量限制坐标定义属性绘制色板更改线条粗细处理鼠标事件处理键盘事件调用事件处理方法完…

Pygame基础教程(一)

写在前面的话: 本系列教程仅有一些在本机调试通过的代码(如代码中发现bug,恳请包涵)。除代码中出现的一些主要注释外,不会出现太多其他文字解释,但是,文章中会给出主要模块的官方文档地址。再次…

Pygame 教程(5):监测游戏时间

本章,你将学习如何监测游戏时间。 导航 上一章:图像传输和绘制文本 下一章:努力更新中…… 文章目录 导航监测时间游戏帧速率实例:绘图性能对比结语 监测时间 在游戏程序中,时常需要随着时间的流逝而做出不同的动作…

Pygame教程系列四:播放音频篇

【前言】 pygame播放音频文件这部分相对来说比较简单,主要是用到pygame.mixer模块,不过也有一些地方需要注意的,咱们直接先看看案例 1、案例效果图 2、案例代码 import pygame from mutagen.mp3 import MP3 # 标识是否退出循环 exitFlag Fa…

python3.8安装pygame_Python3.8安装Pygame教程

注:因为最近想用一下Python做一些简单小游戏的开发作为项目练手之用,而Pygame模块里面提供了大量的有用的方法和属性。今天我们就在之前安装过PyCharm的基础上,安装Pygame,下面是安装的步骤,希望能够帮到大家。 第一步…

Pygame 教程(3):绘制图形

本章,你将学习如何在 Pygame 中绘制图形。 导航 上一章:重要的概念及对象 下一章:图像传输和绘制文本 文章目录 导航抗锯齿draw 模块实例:跟随鼠标的图形创建初始窗口添加变量捕捉鼠标事件绘制图形完整代码 结语 抗锯齿 抗锯齿…

Mac Pycharm导入Pygame教程(超细)

首先先新建一个想要使用Pygame的项目 进入项目后,点击文件(File)——新项目设置(settings) 点击新项目的偏好设置(Preferences for new project ) 随后可以看到 点击Python 编译器&#xff0…

mac python3.8上怎么安装pygame 第三方库_Python3.8安装Pygame教程步骤详解

注:因为最近想用一下python做一些简单小游戏的开发作为项目练手之用,而Pygame模块里面提供了大量的有用的方法和属性。今天我们就在之前安装过PyCharm的基础上,安装Pygame,下面是安装的步骤,希望能够帮到大家。 第一步 安装Python和pip 如果已安装,使用python --version …

Pygame教程系列一:快速入门篇

【简介】 Pygame 是python用来开发视频游戏的游戏引擎,底层主要是SDL库实现,算是目前利用python开发小游戏的一个性能比较高的一个游戏框架 一、安装pygame 使用pip下载安装 pip install pygame二、入门案例详析 1、示例效果 2、示例代码 import os …

pygame教程3

目录 做个小游戏精灵类精灵类介绍使用精灵类 做个小游戏 这个小游戏使用了pygame教程2的知识。 #↓初始化 import pygame,sys from pygame.locals import * pygame.init()#初始化pygame。 screen pygame.display.set_mode((800,600)) pygame.display.set_caption("Hell…

pygame教程2

目录 响应键盘上的事件让画面动起来了解x轴和y轴画圆形圆形动起来 响应键盘上的事件 import pygame#导入pygame。 import sys#导入sys from pygame.locals import *#导入pygame所有的常量,方便以后使用。 pygame.init()#初始化pygame。 screen pygame.display.set…

pygame教程笔记

pygame教程 安装pygameGame Development 1-1: Getting Started with PygameGame Development 1-2: Working with SpritesGame Development 1-3: More About SpritesPygame Shmup Part 1: Player Sprite and ControlsPygame Shmup Part 2: Enemy SpritesPygame Shmup Part 3: Co…