当前位置: 首页 > 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提供了自定…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...