当前位置: 首页 > news >正文

【分布式】: 幂等性和实现方式

【分布式】: 幂等性和实现方式

幂等(idempotent、idempotence)是一个数学与计算机学概念,

常见于抽象代数中。在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“setTrue()”函数就是一个幂等函数,无论多次执行,其结果都是一样的.更复杂的操作幂等保证是利用唯一交易号(流水号)实现。

编程世界

所谓的幂等性,是分布式环境下的一个常见问题,一般是指我们在进行多次操作时,所得到的结果是一样的,即多次运算结果是一致的。

幂等:任意多次执行所产生的影响均与一次执行的影响相同,这是幂等性的核心特点。

任意多次执行所产生的影响均与一次执行的影响相同,这是幂等性的核心特点。其实在我们编程中主要操作就是CURD,其中读取(Retrieve)操作和删除(Delete)操作是天然幂等的,受影响的就是创建(Create)、更新(Update)。

也就是说,用户对于同一操作,无论是发起一次请求还是多次请求,最终的执行结果是一致的,不会因为多次点击而产生副作用。

什么是接口幂等性?

HTTP/1.1中,对幂等性进行了定义。它描述了一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外),即第一次请求的时候对资源产生了副作用,但是以后的多次请求都不会再对资源产生副作用。

这里的副作用是不会对结果产生破坏或者产生不可预料的结果。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。

什么情况下会产生重复提交( 非幂等性 )

以下几种情况会导致非幂等性的结果出现:

  • 连续点击提交两次按钮;
  • 点击刷新按钮;
  • 使用浏览器后退按钮重复之前的操作,导致重复提交表单;
  • 使用浏览器历史记录重复提交表单;
  • 浏览器重复地HTTP请求等

为什么需要实现幂等性

在接口调用时一般情况下都能正常返回信息不会重复提交,不过在遇见以下情况时可以就会出现问题,如:

  1. 前端重复提交表单:在填写一些表格时候,用户填写完成提交,很多时候会因网络波动没有及时对用户做出提交成功响应,致使用户认为没有成功提交,然后一直点提交按钮,这时就会发生重复提交表单请求。

  2. 用户恶意进行刷单:例如在实现用户投票这种功能时,如果用户针对一个用户进行重复提交投票,这样会导致接口接收到用户重复提交的投票信息,这样会使投票结果与事实严重不符。

  3. 接口超时重复提交:很多时候 HTTP 客户端工具都默认开启超时重试的机制,尤其是第三方调用接口时候,为了防止网络波动超时等造成的请求失败,都会添加重试机制,导致一个请求提交多次。

  4. 消息进行重复消费:当使用 MQ 消息中间件时候,如果发生消息中间件出现错误未及时提交消费信息,导致发生重复消费。

使用幂等性最大的优势在于使接口保证任何幂等性操作,免去因重试等造成系统产生的未知的问题。

幂等也有不好的地方

幂等性是为了简化客户端逻辑处理,能放置重复提交等操作,但却增加了服务端的逻辑复杂性和成本,其主要是:

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

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

所以在使用时候需要考虑是否引入幂等性的必要性,根据实际业务场景具体分析,除了业务上的特殊要求外,一般情况下不需要引入的接口幂等性。

Restful API 接口幂等性如何?

http method幂等说明
GET✔️查询读读操作,不修改数据,一般都是幂等
POST新增资源,每次都是新增。
PUT-1. 根据某个值更新,是幂等。
2. 做累加操作更新等,非幂等
DELETE-1. 根据唯一值删除,是幂等
2. 根据条件删除,非幂等

实现幂等的方案

方案适用方法实现复杂度缺点
数据库唯一主键插入操作
删除操作
简单- 只能用于插入操作
- 只能用于存在唯一主键场景
数据库乐观锁更新操作简单- 只能更新操作
- 表中新增字段
请求序列号插入操作
删除操作
更新操作
简单- 需要保证下游生成唯一序号
- 需要redi第三方存储生成token
防重Token令牌插入操作
删除操作
更新操作
适中需要redi第三方存储生成token

方案原理讲解

数据库唯一主键实现幂等性

数据库唯一主键的实现主要是利用数据库中主键唯一约束的特性,一般来说唯一主键比较适用于“插入”时的幂等性,其能保证一张表中只能存在一条带该唯一主键的记录。

使用数据库唯一主键完成幂等性时需要注意的是,该主键一般来说并不是使用数据库中自增主键,而是使用分布式 ID 充当主键,这样才能能保证在分布式环境下 ID 的全局唯一性。

关键步骤:需要生成全局唯一主键 ID

主要流程如下:

  1. 客户端执行创建请求,调用服务端接口。

  2. 服务端执行业务逻辑,生成一个分布式 ID,将该 ID 充当待插入数据的主键,然 后执数据插入操作,运行对应的 SQL 语句。

  3. 服务端将该条数据插入数据库中,如果插入成功则表示没有重复调用接口。如果抛出主键重复异常,则表示数据库中已经存在该条记录,返回错误信息到客户端。

数据库乐观锁实现幂等性

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

关键步骤: 需要数据库对应业务表中添加额外字段

为了每次执行更新时防止重复更新,确定更新的一定是要更新的内容,我们通常都会添加一个 version 字段记录当前的记录版本,这样在更新时候将该值带上,那么只要执行更新操作就能确定一定更新的是某个对应版本下的信息。

这样每次执行更新时候,都要指定要更新的版本号,如下操作就能准确更新 version=5 的信息:

UPDATE my_table SET price=price+50,version=version+1 WHERE id=1 AND version=5  

上面 WHERE 后面跟着条件 id=1 AND version=5 被执行后,id=1version 被更新为 6,所以如果重复执行该条 SQL 语句将不生效,因为 id=1 AND version=5 的数据已经不存在,这样就能保住更新的幂等,多次更新对结果不会产生影响。

防重 Token 令牌实现幂等性

针对客户端连续点击或者调用方的超时重试等情况,例如提交订单,此种操作就可以用 Token 的机制实现防止重复提交。

简单的说就是调用方在调用接口的时候先向后端请求一个全局 ID(Token),请求的时候携带这个全局 ID 一起请求(Token 最好将其放到 Headers 中),后端需要对这个 Token 作为 Key,用户信息作为 ValueRedis 中进行键值内容校验,如果 Key 存在且 Value 匹配就执行删除命令,然后正常执行后面的业务逻辑。如果不存在对应的 KeyValue 不匹配就返回重复执行的错误信息,这样来保证幂等操作。

NOTE : 获取Token只做一次,而非每次调用资源请求时都获取。
获取一次TOKEN可以随页面加载的时候去获取,或调用资源接口前获取一次。
在资源接口一次或多次请求时,使用的token是一样的。如果刷新界面或切换资源id,可以重新获取token

关键步骤

需要生成全局唯一 Token
需要使用第三方组件 Redis 进行数据效验

在这里插入图片描述

  1. 服务端提供获取 Token 的接口,该 Token 可以是一个序列号,也可以是一个分布式 ID 或者 UUID 串。

  2. 客户端调用接口获取 Token,这时候服务端会生成一个 Token 串。

  3. 然后将该串存入 Redis 数据库中,以该 Token 作为 Redis 的键(注意设置过期时间)。

  4. 将 Token 返回到客户端,客户端拿到后应存到表单隐藏域中。

  5. 客户端在执行提交表单时,把 Token 存入到 Headers 中,执行业务请求带上该 Headers

  6. 服务端接收到请求后从 Headers 中拿到 Token,然后根据 Token 到 Redis 中查找该 key 是否存在。

  7. 服务端根据 Redis 中是否存该 key 进行判断,如果存在就将该 key 删除,然后正常执行业务逻辑。如果不存在就抛异常,返回重复提交的错误信息。

注意,在并发情况下,执行 Redis 查找数据与删除需要保证原子性,否则很可能在并发下无法保证幂等性。其实现方法可以使用分布式锁或者使用 Lua 表达式来注销查询与删除操作。

下游传递唯一序列号实现幂等性

所谓请求序列号,其实就是每次向服务端请求时候附带一个短时间内唯一不重复的序列号,该序列号可以是一个有序 ID,也可以是一个订单号,一般由下游生成,在调用上游服务端接口时附加该序列号和用于认证的 ID
当上游服务器收到请求信息后拿取该 序列号 和下游 认证ID 进行组合,形成用于操作 Redis 的 Key,然后到 Redis 中查询是否存在对应的 Key 的键值对,根据其结果:

  1. 如果存在,就说明已经对该下游的该序列号的请求进行了业务处理,这时可以直接响应重复请求的错误信息。
  2. 如果不存在,就以该 Key 作为 Redis 的键,以下游关键信息作为存储的值(例如下游商传递的一些业务逻辑信息),将该键值对存储到 Redis 中 ,然后再正常执行对应的业务逻辑即可。

关键步骤

  • 要求第三方传递唯一序列号;

  • 需要使用第三方组件 Redis 进行数据效验;

在这里插入图片描述

  1. 下游服务生成分布式 ID 作为序列号,然后执行请求调用上游接口,并附带唯一序列号与请求的认证凭据ID
  2. 上游服务进行安全效验,检测下游传递的参数中是否存在序列号凭据ID
  3. 上游服务到 Redis 中检测是否存在对应的序列号认证ID组成的 Key,如果存在就抛出重复执行的异常信息,然后响应下游对应的错误信息。如果不存在就以该序列号认证ID组合作为 Key,以下游关键信息作为 Value,进而存储到 Redis 中,然后正常执行接来来的业务逻辑。

上面步骤中插入数据到 Redis 一定要设置过期时间。这样能保证在这个时间范围内,如果重复调用接口,则能够进行判断识别。如果不设置过期时间,很可能导致数据无限量的存入 Redis,致使 Redis 不能正常工作。

实现

基于 防重Token令牌方案 实现

代码仓库:
无难事者若执 / Spring Utils · GitCode

相关文章:

【分布式】: 幂等性和实现方式

【分布式】: 幂等性和实现方式 幂等(idempotent、idempotence)是一个数学与计算机学概念, 常见于抽象代数中。在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是…...

idea 设置serlvet 类模板(快捷生成servlet类)

我的版本是idea2020.3.4,博客中有相应安装教程,其他版本设置类似: 1.选择文件-->设置 2.选择编辑器-->文件和代码模板-->其他 3.选择Web-->Servlet Annotated Class.java-->复制相应模板,下面顺便设置了注释模板 …...

SpringBoot自动配置原理解析 | 京东物流技术团队

1: 什么是SpringBoot自动配置 首先介绍一下什么是SpringBoot,SpringBoost是基于Spring框架开发出来的功能更强大的Java程序开发框架,其最主要的特点是:能使程序开发者快速搭建一套开发环境。SpringBoot能将主流的开发框架(例如Sp…...

AOP 笔记

AOP【面向切面编程】 作用:在不惊动原始设计的基础上进行功能增强。 无侵入式编程 连接点:程序执行的任意位置,SpringAOP中,理解为方法的执行。 切入点:匹配连接点的式子,要追加功能的方法 通知(写在通…...

微信小程序导航退回及跳转 传参(navigateBack,navigateTo)

一、uniapp navigateBack 退回上一级 当前页面-传递参数 uni.$emit(update, params)uni.navigateBack({delta: 1});退回的页面-接收参数 可以写在 onLoad 和 onShow 里面 onLoad(o) {uni.$on(update, function(e) {//参数e}}onShow() {}返回前两级 uni.navigateBack({delta: 2}…...

python实例代码介绍python基础知识

TODO: 知识点仍有待整理 import 使用 import 关键字可以让你选择性地导入所需的模块,而不必导入整个模块库。这样可以减少内存占用和加载时间,尤其是当你只需要使用模块中的某些功能时。 同时,使用 import 可以提高代码的可读性和可维护性&…...

【每日一题】掷骰子等于目标和的方法数

文章目录 Tag题目来源题目解读解题思路方法一:动态规划 写在最后 Tag 【动态规划】【数组】 题目来源 1155. 掷骰子等于目标和的方法数 题目解读 你手里有 n 个一样的骰子,每个骰子都有 k 个面,分别标号 1 到 n。给定三个整数 n&#xff0…...

霸王条款惹品牌争议,京东双11站在商家对立面?

作者 | 江北 来源 | 洞见新研社 双11活动第一天,京东就站上了风口浪尖。 与烘焙烤箱品牌海氏的话题接连登上微博热搜,海氏控诉京东滥用市场竞争地位,破坏市场竞争秩序。在海氏的声明中,京东的行为让吃瓜群众大开眼界&#xff1a…...

深度神经网络为何成功?其中的过程、思想和关键主张选择

LeNet(1989)在小数据集上取得了很好的效果,但是在更大、更真实地数据集上训练卷积神经网络地性能和可行性还有待研究。 与神经网络竞争的是传统机器学习方法,比如SVM(支持向量机)。这个阶段性能比神经网络方…...

什么是服务器节点?

一.服务器节点的概念: 服务器节点是一种服务器装置,节点服务器是针对服务器集群来说的。主要应用在WEB、FTP等等的服务上。所以节点服务器并不是单指某一种服务器。它由多个节点和管理装置整体的管理单元构成,其特征在于:各节点具…...

水电站与数据可视化:洞察未来能源趋势的窗口

在信息时代的浪潮中,数据可视化正成为推动能源领域发展的重要工具。今天,我们将带您一起探索水电站与数据可视化的结合,如何成为洞察未来能源趋势的窗口。水电站作为传统能源领域的重要组成部分,它的运行与管理涉及大量的数据。然…...

Mac运行Docker报错

Mac运行Docker报错 📔 千寻简笔记介绍 千寻简笔记已开源,Gitee与GitHub搜索chihiro-notes,包含笔记源文件.md,以及PDF版本方便阅读,且是用了精美主题,阅读体验更佳,如果文章对你有帮助请帮我点…...

代码 $(“.btn“).click(function(){ 和代码 $(document).ready(function() 有啥区别?

看下面的内容前可以先看下博文:https://blog.csdn.net/wenhao_ir/article/details/134029389 $(".btn").click(function(){...}) 和 $(document).ready(function(){...}) 是两种不同的 jQuery 事件处理方式,它们有不同的用途和时机&#xff1…...

【nodejs脚本】为文件夹中的所有node项目执行命令 npm install 并收集error日志

目录 im 下有很多的node项目,我需要批量为这些项目执行 npm install,另外npm的error信息需要单独收集至log文件中 var fs require(fs); var util require(util); var exec util.promisify(require(child_process).exec);var projectsDirectory .; v…...

非父子组件通信-发布订阅模式

发布订阅模式其实与vue无关,完全是ES6的代码,但是它可以通过这种模式实现非父子组件的通信 store.js文件 首先创建一个store.js文件,用于提供发布与订阅方法 export default {datalist: [], //存放带一个参数的函数集合//订阅subscribe(fu…...

iPhone手机分辨率整理

手机机型(iPhone)屏幕尺寸 (inch)逻辑分辨率(pt)设备分辨率(px)缩放因子(Scale Factor)竖屏安全区域(safeAreaInsets)纵横比(Aspect ratio)像素密度(ppi)2G/3G/3GS3.5320*480320*4801xtop:20 bottom:03:21654/4(s)3.5320*480640*9602xtop:20 bottom:016:…...

【linux】SourceForge 开源软件开发平台和仓库

在linux上面安装服务和工具。我们经常会下载安装包。今天推荐一个网站。 SourceForge 开源软件开发平台和仓库 ​ 全球最大开源软件开发平台和仓库 SourceForge.net,又称SF.net,是开源软件开发者进行开发管理的集中式场所。 SourceForge.net由VA Softwa…...

LabVIEW应用开发——控件的使用(四)

接上文,这篇介绍时间控件。 LabVIEW应用开发——控件的使用(三) 1、时间控件Time Stamp control 在日常软件开发场景中,时间也是一种常用的控件,用于表达当前时间的显示、对下设置时间、时间同步等等场景。LabVIEW专门…...

MySQL - mvcc

mvcc 是什么? MVCC(多版本并发控制)是一种数据库并发控制机制,旨在提高数据库的并发性,避免锁定操作,从而减少等待和提高性能。MVCC 主要解决数据库读写操作之间的线程安全问题。 MVCC 主要有两种读取数据…...

SpringMVC 异常处理器

1、基于配置的异常处理 SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver SpringMVC提供了自定…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

Cursor实现用excel数据填充word模版的方法

cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...