【Spring Cloud系列】- 分布式系统中实现幂等性的几种方式
【Spring Cloud系列】- 分布式系统中实现幂等性的几种方式
文章目录
- 【Spring Cloud系列】- 分布式系统中实现幂等性的几种方式
- 一、概述
- 二、什么是幂等性
- 三、幂等性需关注几个重点
- 四、幂等性有什么用
- 五、常见用来保证幂等的手段
- 5.1 MVCC方案
- 5.2 去重表
- 5.3 去重表
- 5.4 select + insert
- 5.5 状态机幂等
- 5.6 token机制,防止页面重复提交
- 5.7 对外提供接口的api如何保证幂等
- 5.7 全局唯一ID
- 5.8 分布式锁
- 六、总结

一、概述
在开发订单系统时,我们常遇见支付问题,既用户购买商品后支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额发现多扣钱了,流水记录也变成了两条。在以前的单应用系统中,我们只需要把数据操作放入事务中即可,发生错误立即回滚,但是再响应客户端的时候也有可能出现网络中断或者异常问题。如果保证一个订单从创建到支付成功整个订单生命周期中,数据是一致,不因异常而改变。这种机制就需要用到幂等性。
二、什么是幂等性
幂等是一个数学与计算机的概念,常见于常见于抽象代数中。
幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。
幂等性:任意多次执行对资源本身所产生的影响均与一次执行的影响相同
接口的幂等性实际上就是接口可重复调用,在调用方多次调用的情况下,接口最终得到的结果是一致的,有些接口可以天然的幂等性,比如查询接口,调用一次和调用多次,对系统得到的结果,是一样的。并不会因为调用的次数改变查询的结果。
插入(INSERT)和修改(UPDATE)方法是非幂等性的,需要通过机制在需要的场景处理以确保多次执行无副作用。
删除(delete)执行一次或多次都是结果为空(即结果一致),并且无副作用,所以在根据主键ID删除可以认为是(伪)幂等性的,根据非主键删除的如果多次执行无副作用(都是把数据删除),也可以认为是(伪)幂等性。
三、幂等性需关注几个重点
- 幂等不仅仅只是一次(或多次)请求对资源没有副作用(比如查询数据库操作,没有增删改,因此没有对数据库有任何影响)
- 幂等还包括第一次请求的时候对资源产生了副作用,但是以后的多次请求都不会再对资源产生副作用。
- 幂等关注的是以后的多次请求是否对资源产生的副作用,而不关注结果。
- 网络超时等问题,不是幂等性的讨论范围。
幂等性是系统服务对外一种承诺(而不是实现),承诺只要调用接口成功,外部多次调用对系统的影响是一致的。声明为幂等的服务会认为外部调用失败是常态,并且失败之后必然会有重试。
四、幂等性有什么用
业务开发中,经常会遇到重复提交的情况,无论是由于网络问题无法收到请求结果而重新发起请求,或是前端的操作抖动而造成重复提交情况。 在交易系统,支付系统这种重复提交造成的问题有尤其明显,比如:
- 用户在APP上连续点击了多次提交订单,后台应该只产生一个订单;
- 向支付宝发起支付请求,由于网络问题或系统BUG重发,支付宝应该只扣一次钱。 很显然,声明幂等的服务认为,外部调用者会存在多次调用的情况,为了防止外部多次调用对系统数据状态的发生多次改变,将服务设计成幂等。
- 出库单反复请求产生多个出库单信息。
五、常见用来保证幂等的手段
5.1 MVCC方案
多版本并发控制,该策略主要使用update with condition(更新带条件来防止)来保证多次外部请求调用对系统的影响是一致的。在系统设计的过程中,合理的使用乐观锁,通过version或者updateTime(timestamp)等其他条件,来做乐观锁的判断条件,这样保证更新操作即使在并发的情况下,也不会有太大的问题。例如
select * from tablename where condition=#condition# //取出要跟新的对象,带有版本versoin
update tableName set name=#name#,version=version+1 where version=#version#
在更新的过程中利用version来防止,其他操作对对象的并发更新,导致更新丢失。为了避免失败,通常需要一定的重试机制。
5.2 去重表
在插入数据的时候,插入去重表,利用数据库的唯一索引特性,保证唯一的逻辑。
这种方法适用于在业务中有唯一标的插入场景中,比如在以上的支付场景中,如果一个订单只会支付一次,所以订单ID可以作为唯一标识。这时,我们就可以建一张去重表,并且把唯一标识作为唯一索引,在我们实现时,把创建支付单据和写入去去重表,放在一个事务中,如果重复创建,数据库会抛出唯一约束异常,操作就会回滚。
5.3 去重表
select for update,整个执行过程中锁定该订单对应的记录。注意:这种在DB读大于写的情况下尽量少用。
5.4 select + insert
并发不高的后台系统,或者一些任务JOB,为了支持幂等,支持重复执行,简单的处理方法是,先查询下一些关键数据,判断是否已经执行过,在进行业务处理,就可以了。注意:核心高并发流程不要用这种方法。
5.5 状态机幂等
在设计单据相关的业务,或者是任务相关的业务,肯定会涉及到状态机,就是业务单据上面有个状态,状态在不同的情况下会发生变更,一般情况下存在有限状态机,这时候,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。
这种方法适合在有状态机流转的情况下,比如就会订单的创建和付款,订单的付款肯定是在之前,这时我们可以通过在设计状态字段时,使用int类型,并且通过值类型的大小来做幂等,比如订单的创建为1000,付款成功为1。付款失败为999。
5.6 token机制,防止页面重复提交
业务要求:页面的数据只能被点击提交一次,
发生原因:由于重复点击或者网络重发,或者nginx重发等情况会导致数据被重复提交
解决办法:
- 集群环境:采用token加redis(redis单线程的,处理需要排队)
- 单JVM环境:采用token加redis或token加jvm内存
处理流程:
- 数据提交前要向服务的申请token,token放到redis或jvm内存,token有效时间
- 提交后后台校验token,同时删除token,生成新的token返回
- token特点:要申请,一次有效性,可以限流
5.7 对外提供接口的api如何保证幂等
如微信提供的付款接口:需要接入商户提交付款请求时附带:source来源,seq序列号。source+seq在数据库里面做唯一索引,防止多次付款,(并发时,只能处理一个请求)
**总结:**幂等性应该是合格程序员的一个基因,在设计系统时,是首要考虑的问题,尤其是在像支付宝,银行,互联网金融公司等涉及的都是钱的系统,既要高效,数据也要准确,所以不能出现多扣款,多打款等问题,这样会很难处理,用户体验也不好 。
5.7 全局唯一ID
如果使用全局唯一ID,就是根据业务的操作和内容生成一个全局ID,在执行操作前先根据这个全局唯一ID是否存在,来判断这个操作是否已经执行。如果不存在则把全局ID,存储到存储系统中,比如数据库、redis等。如果存在则表示该方法已经执行。
从工程的角度来说,使用全局ID做幂等可以作为一个业务的基础的微服务存在,在很多的微服务中都会用到这样的服务,在每个微服务中都完成这样的功能,会存在工作量重复。另外打造一个高可靠的幂等服务还需要考虑很多问题,比如一台机器虽然把全局ID先写入了存储,但是在写入之后挂了,这就需要引入全局ID的超时机制。
使用全局唯一ID是一个通用方案,可以支持插入、更新、删除业务操作。但是这个方案看起来很美但是实现起来比较麻烦,下面的方案适用于特定的场景,但是实现起来比较简单。
5.8 分布式锁
在进入方法时,先去获取锁,假如获取到锁,就继续后面的流程。假如没有获取到锁,就等待锁的释放直到获取到锁。当执行完方法时,释放锁。当然,锁要设个超时时间,防止意外没有释放到锁。它用来解决分布式系统的幂等性,常用的实现方案是 redis 和 zookeeper 等工具。
六、总结
幂等性增加了额外控制幂等的业务逻辑,复杂化了业务功能,把并行执行的功能改为串行执行,降低了执行效率。
幂等性虽然复杂化了业务功能和降低了执行效率,但为了保证系统的正确性,是必要的。就上面更新 X 的例子,在单台服务器上,给那段代码加上锁,并给 X 设为 volatile,就保证来数据的正确性了。在分布式环境下并且 X 是从数据库或者文件里查询出来的,用上面加锁的方式实现就不能保证数据的正确性了,这时候就需要用到分布式锁了。所以,保证方法或接口的幂等性是非常有必要的,因为数据是不能出现任何问题的。
相关文章:

【Spring Cloud系列】- 分布式系统中实现幂等性的几种方式
【Spring Cloud系列】- 分布式系统中实现幂等性的几种方式 文章目录 【Spring Cloud系列】- 分布式系统中实现幂等性的几种方式一、概述二、什么是幂等性三、幂等性需关注几个重点四、幂等性有什么用五、常见用来保证幂等的手段5.1 MVCC方案5.2 去重表5.3 去重表5.4 select in…...

2023.8.26-2023.9.3 周报【3D+GAN+Diffusion基础知识+训练测试】
目录 学习目标 学习内容 学习时间 学习产出 学习目标 1. 3D方向的基础知识 2. 图像生成的基础知识(GAN \ Diffusion) 3. 训练测试GAN和Diffusion 学习内容 1. 斯坦福cv课程-3D (网课含PPT) 2. sjtu生成模型课件 3. ge…...

如何使用CSS创建渐变阴影?
随着网络的不断发展,制作漂亮的 UI 是提高客户在网站上的参与度的最重要的工作之一。改善前端外观的方法之一是在 CSS 中应用渐变阴影。应用渐变阴影的两种最重要的方法是线性渐变和径向渐变。 渐变阴影可用于吸引用户对特定信息的注意力,应用悬停或焦点…...

perl send HTTP Request
perl send HTTP Request 使用Perl进行发送HttP请求 use LWP::UserAgent; use HTTP::Request; use HTTP::Headers; use JSON::PP;my $test_url "htttp://127.0.0.1:8080/update/";sub sendHttp{my $user_agent LWP::UserAgent->new(timeout>60);my ($url, $…...

阿里云CDN缓存预热与刷新以及常见的故障汇总
文章目录 1.为CDN缓存的文件增加过期时间2.CDN缓存预热配置3.CDN缓存刷新配置4.常见故障 CDN缓存预热指的是主动将要缓存的文件推送到全国各地的CDN边缘加速器上,减少回源率,提供命中率。 缓存刷新指的是后期上传了同名的文件,之前的缓存已经…...

Oracle创建控制列表ACL(Access Control List)
Oracle创建控制列表ACL(Access Control List) Oracle ACL简介一、先登陆163邮箱设置开启SMTP。二、Oracle ACL控制列表处理(一)创建ACL(create_acl)(二)添加ACL权限(add_…...

3D模型转换工具HOOPS Exchange助力打造虚拟现实应用程序
挑战: 支持使用各种 CAD 系统和 CAD 文件格式的客户群向可视化硬件提供快速、准确的数据加载提供对详细模型信息的访问,同时确保高帧率性能 解决方案: HOOPS Exchange领先的CAD数据转换工具包 结果: 确保支持来自领先工程软件…...

python web GUI框架-NiceGUI 教程(二)
python web GUI框架-NiceGUI 教程(二) streamlit可以在一些简单的场景下仍然推荐使用,但是streamlit实在不灵活,受限于它的核心机制,NiceGUI是一个灵活的web框架,可以做web网站也可以打包成独立的exe。 基…...

RT_Thread内核机制学习(二)
对于RTT来说,每个线程创建时都自带一个定时器 rt_err_t rt_thread_sleep(rt_tick_t tick) {register rt_base_t temp;struct rt_thread *thread;/* set to current thread */thread rt_thread_self();RT_ASSERT(thread ! RT_NULL);RT_ASSERT(rt_object_get_type(…...

线性代数的学习和整理12: 矩阵与行列式,计算上的差别对比
目录 1 行列式和矩阵的比较 2 简单总结矩阵与行列式的不同 3 加减乘除的不同 3.1 加法不同 3.2 减法不同 3.3 标量乘法/数乘 3.3.1 标准的数乘对比 3.3.2 数乘的扩展 3.4 乘法 4 初等线性变换的不同 4.1 对矩阵进行线性变换 4.2 对行列式进行线性变换 1 行列式和…...

2023年MySQL核心技术面试第一篇
目录 一 . 存储:一个完整的数据存储过程是怎样的? 1.1 数据存储过程 1.1.1 创建MySQl 数据库 1.1.1.1 为什么我们要先创建一个数据库,而不是直接创建数据表? 1.1.1.2基本操作部分 1.2 选择索引问题 二 . 字段:这么多的…...

linux启动jar 缺失lib
linux启动jar包时,找不到报错 [rootebs-141185 xl-admin]# java -Djava.library.path/home/kabangke/xl-admin/lib -jar /home/kabangke/xl-admin/xl-admin.jar Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/web/se…...

【Bash】常用命令总结
文章目录 1. 文件查询1.1 查看文件夹内(包含子文件夹)文件数量1.2 查看文件夹大小 任务简介: 对bash常用命令进行总结。 任务说明: 对平时工作中使用bash的相关命令做一个记录和说明,方便以后查阅。 1. 文件查询 1.…...

小研究 - Java虚拟机性能及关键技术分析
利用specJVM98和Java Grande Forum Benchmark suite Benchmark集合对SJVM、IntelORP,Kaffe3种Java虚拟机进行系统测试。在对测试结果进行系统分析的基础上,比较了不同JVM实现对性能的影响和JVM中关键模块对JVM性能的影响,并提出了提高JVM性能的一些展望。…...

Repo manifests默认default.xml清单文件中的各个标签详解
Repo简介 “Repo” 是一个用于管理多个Git存储库的工具,通常与Google的Android开发项目一起使用。它允许您在一个命令下轻松地进行多个Git存储库的同步、下载和管理。 repo下载安装 从清华镜像源下载 mkdir ~/bin PATH~/bin:$PATH curl https://mirrors.tun…...

javacv基础02-调用本机摄像头并预览摄像头图像画面视频
引入架包: <dependency><groupId>org.openpnp</groupId><artifactId>opencv</artifactId><version>4.5.5-1</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId…...

【Nginx21】Nginx学习:FastCGI模块(三)缓冲区与响应头
Nginx学习:FastCGI模块(三)缓冲区与响应头 缓存相关的内容占了 FastCGI 模块将近一小半的内容,当然,用过的人可能不多。而今天的内容说实话,我平常也没怎么用过。第一个是缓冲区相关的知识,其实…...

正则表达式(常用字符简单版)
量词 字符类 边界匹配 分组和捕获 特殊字符 字符匹配 普通字符:普通字符按照字面意义进行匹配,例如匹配字母 "a" 将匹配到文本中的 "a" 字符。元字符:元字符具有特殊的含义,例如 \d 匹配任意数字字符…...

从零开始学习Python爬虫:详细指南
导言: 随着互联网的迅速发展,大量的数据可供我们利用。而Python作为一种简单易学且功能强大的编程语言,被广泛应用于数据分析和处理。学习Python爬虫技术,能够帮助我们从互联网上获取数据,并进行有效地分析和利用。本文…...

分布式计算框架:Spark、Dask、Ray
目录 什么是分布式计算 分布式计算哪家强:Spark、Dask、Ray 2 选择正确的框架 2.1 Spark 2.2 Dask 2.3 Ray 什么是分布式计算 分布式计算是一种计算方法,和集中式计算是相对的。 随着计算技术的发展,有些应用需要非常巨大的计算能力才…...

什么是伪类链(Pseudo-class Chaining)?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Pseudo-class Chaining⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚刚…...

每日一题:leetcode 57 插入区间
给你一个 无重叠的 ,按照区间起始端点排序的区间列表。 在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。 示例 1: 输入:intervals [[1,3…...

第五节:实现自己的第一个environment
本专栏是强化学习运用在买卖股票之上的入门学习内容。 主要解决强化学习代码落地和代码实践,不需要学习相关数学原理,直观简单的带领读者入门强化学习炒股。 查看本专栏完整内容,请访问:https://blog.csdn.net/windanchaos/category_12391143.html 本文发布地址:https://b…...

无套路,财务数据分析-多组织损益表分析分享
在报表众多的财务数据分析中,损益表是老板们最关注的报表,特别是当有多组织时,损益表的分析就变得更加重要了。以前受限于数据分析工具,做损益表分析时很难做到多维度灵活分析,但随着BI数据可视化工具的发展࿰…...

Java并发编程第6讲——线程池(万字详解)
Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池,本篇文章就详细介绍一下。 一、什么是线程池 定义:线程池是一种用于管理和重用线程的技术(池化技术),它主…...

AI + Milvus:将时尚应用搭建进行到底
在上一篇文章中,我们学习了如何利用人工智能技术(例如开源 AI 向量数据库 Milvus 和 Hugging Face 模型)寻找与自己穿搭风格相似的明星。在这篇文章中,我们将进一步介绍如何通过对上篇文章中的项目代码稍作修改,获得更…...

归并排序(Java 实例代码)
目录 归并排序 一、概念及其介绍 二、适用说明 三、过程图示 四、Java 实例代码 MergeSort.java 文件代码: 归并排序 一、概念及其介绍 归并排序(Merge sort)是建立在归并操作上的一种有效、稳定的排序算法,该算法是采用分…...

【VUE】数字动态变化到目标值-vue-count-to
vue-count-to是一个Vue组件,用于实现数字动画效果。它可以用于显示从一个数字到另一个数字的过渡动画。 插件名:vue-count-to 官方仓库地址:GitHub - PanJiaChen/vue-countTo: Its a vue component that will count to a target number at a…...

Mysql /etc/my.cnf参数详解(二)
#buffer相关 #buffer pool根据实际内存大小调整,标准为物理内存的50% innodb_buffer_pool_size15996M //默认值128M,innodb_buffer_pool_size | 134217728 key_buffer_size 33554432 #根据物理内存大小设置 确保每个instance内的内存2G左右 <5000 1,>5000 &…...

AUTOSAR规范与ECU软件开发(实践篇)6.10AUTOSAR操作系统概念与配置方法介绍(下)
目录 2、 RTA-OS工程创建 3、 AUTOSAR操作系统配置方法 (1) 描述文件导入 (2) Counter配置...