01-幂等性解释,问题及常用解决方案
目录
1. 幂等性简介
2. 后端如何解决幂等性问题
2.1 数据库层面
-> 2.1.1 防重表
-> 2.1.2 数据库悲观锁(不建议,容易出现死锁情况)
-> 2.1.3 数据库乐观锁
-> 2.1.4 乐观锁CAS算法原理
2.2 锁层面
2.3 幂等性token层面
-> 2.3.1 简介文字描述:
-> 2.3.2 简介图示:
编辑
-> 2.3.3 创建注解
-> 2.3.4 创建请求拦截器
----->方案一: 使用incr保证原子性
----->方案一代码: 拦截器
====>方案二: 使用分布式锁, 这篇文章暂不介绍
-> 2.3.5 获取幂等token代码
-> 2.3.6 接口测试
-> 2.3.7 使用测试工具进行测试
-> 2.3.8 幂等校验token的优,缺点
-----> 2.3.8.1 使用的优点:
-----> 2.3.8.2 使用的缺点:
3. 前端如何操作来避免幂等问题
-> 3.1前端防重
-> 3.2 PRG模式
1. 幂等性简介
分布式或微服务思想实现系统架构设计中, 服务相互调用,可能存在服务调用延迟或失败情况。服务端可能会进行多次点击提交。如果这样请求多次的话,那最终处理的数据结果就一定要保证统一,如 订单创建,支付扣款,库存扣减,物流发货等。此时就需要通过保证业务幂等性方案来完成。
2. 后端如何解决幂等性问题
2.1 数据库层面
-> 2.1.1 防重表
创建唯一索引: 多用于详情信息, 个人账户等业务,如果并发情况下出现多条,数据库报异常
创建唯一组合索引: 可以创建组合索引来保证唯一性
-> 2.1.2 数据库悲观锁(不建议,容易出现死锁情况)
加入悲观锁, for update, 有主键索引/明确索引就是行锁, 不然就是表锁
文章介绍跳转: -> mysql for update 详细介绍
-> 2.1.3 数据库乐观锁
占用数据库资源, 需要有版本号, 先查询版本号作为老的版本(oldVersion)存起来, 进行修改操作, 然后把这个值也进行修改+1 条件是version= oldVersion, 如果version不正确 修改失败
version= 5 100个线程同时取到5 其中一个修改成功version=6, 则其他99个失败
-> 2.1.4 乐观锁CAS算法原理
简介: compare and swap:比较与交换 , 核心即为 冲突检测和数据更新
文章介绍跳转: ->
2.2 锁层面
锁系列文章跳转(待定) ->
2.3 幂等性token层面
-> 2.3.1 简介文字描述:
1. 先制作幂等性token生成器, 创建token
2. 创建注解, 写入默认值 表示是否开启校验
3. 写拦截器 判断接口上是否有注解 如果没有,放行
4. 有注解的, 判断请求头中是否存在幂等token, 不存在 拦截请求
5. 存在, 判断幂等token真实性(是否过期), 不存在或incr数值异常 删除幂等token 拦截请求
6. 一切正常,删除token,放行请求, 进入controller
-> 2.3.2 简介图示:
-> 2.3.3 创建注解
package *;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 幂等性校验* 需要多线程测试后* 查出是否需要优化* @author pzy* @version 0.1.1 测试版本*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIdempotentAnn {boolean value() default true;
}
-> 2.3.4 创建请求拦截器
----->方案一: 使用incr保证原子性
简介: Redis 的 INCR 命令可以将存储在指定键中的数字值增加 1。INCR 命令是一个原子性操作, 意味着它在执行过程中不会被中断。 例如,假设您有一个名为 "counter" 的键,存储的值为 0。如果您使用 INCR 命令将其 增加 1,那么这个键的值就会变为 1。如果再次使用 INCR 命令将其增加 1,那么这个键的值 就会变为 2。 由于INCR 命令是原子性的,因此可以在并发环境下使用
----->方案一代码: 拦截器
package *;import *;
import *;
import *;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;import static com.aisce.common.constant.RedisKeyConstants.IDEMPOTENT_TOKEN;/*** 幂等性校验拦截器(用哪个工程粘过去即可)* 使用方式:* <p>* <p>* 后端操作: @ApiIdempotentAnn注解 添加到接口位置即可 其他无需操作* <p>* 前端操作: 在请求头中添加 idempotent参数* 示例: "idempotent": "fcc8b373-b867-4d6c-9915-e3bd668db7b6"** @author pzy* @version 0.1.1 初期版本*/
@Slf4j
@Component
@Order(2)
public class ApiIdempotentInterceptor extends HandlerInterceptorAdapter {@Autowiredprivate StringRedisTemplate stringRedisTemplate;/*** 前置拦截器* 在方法被调用前执行。在该方法中可以做类似校验的功能。如果返回true,则继续调用下一个拦截器。如果返回false,则中断执行,* 也就是说我们想调用的方法 不会被执行,但是你可以修改response为你想要的响应。*/@SuppressWarnings(value = {"all"})@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("===> 执行链2 执行");//如果handler不是和HandlerMethod类型,则返回trueif (!(handler instanceof HandlerMethod)) {return true;}//转化类型final HandlerMethod handlerMethod = (HandlerMethod) handler;//获取方法类final Method method = handlerMethod.getMethod();// 判断当前method中是否有这个注解boolean methodAnn = method.isAnnotationPresent(ApiIdempotentAnn.class);//如果有幂等性注解if (methodAnn && method.getAnnotation(ApiIdempotentAnn.class).value()) {// 需要实现接口幂等性//检查token//1.获取请求的接口方法boolean result = checkToken(request);//如果token有值,说明是第一次调用if (result) {//则放行return super.preHandle(request, response, handler);} else {//如果token没有值,则表示不是第一次调用,是重复调用response.setContentType("application/json; charset=utf-8");PrintWriter writer = response.getWriter();writer.print(new ObjectMapper().writeValueAsString(ResultResponse.error("重复调用,已拦截!")));writer.close();response.flushBuffer();log.error(String.format("===> %s 重复调用%s,已拦截!", request.getRemoteAddr(), request.getRequestURI()));return false;}}//否则没有该自定义幂等性注解,则放行return super.preHandle(request, response, handler);}/*** 幂等token的校验方法*/private boolean checkToken(HttpServletRequest request) {//从请求头对象中获取 幂等性校验token<idempotent>String idempotentToken = request.getHeader("idempotent");//如果不存在,则返回false,说明是重复调用if (StringUtils.isBlank(idempotentToken)) {return false;}//格式化幂等校验tokenidempotentToken = String.format(IDEMPOTENT_TOKEN, idempotentToken);if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(idempotentToken))) {return false;}//保证原子性<方式一> incrLong num = stringRedisTemplate.opsForValue().increment(idempotentToken);if (num != null && num.intValue() != 2) {//只要逻辑不对 删除key, 阻止请求delIdempotentToken(idempotentToken);return Boolean.FALSE;}//否则就是存在,存在则把redis里删除token//return Boolean.TRUE;return delIdempotentToken(idempotentToken);}/*** 删除幂等校验token** @param idempotentToken* @return*/private Boolean delIdempotentToken(String idempotentToken) {return Boolean.TRUE.equals(stringRedisTemplate.delete(idempotentToken));}}
====>方案二: 使用分布式锁, 这篇文章暂不介绍
下篇文章预告: 分布式锁基本使用方式,以及配合注解实现快速加锁
-> 2.3.5 获取幂等token代码
/*** 前端获取token,然后把该token放入请求的header中** @return*/@ApiOperation("获取幂等token")@GetMapping("/getIdempotentToken")public ResultResponse getIdempotentToken() {log.info("===> 获取幂等token <===");String idempotent = UUID.randomUUID().toString();stringRedisTemplate.opsForValue().set(String.format(IDEMPOTENT_TOKEN,idempotent),"1", Duration.ofSeconds(60*2));//2分钟 不随机数了 两分钟幂等失效拦截return ResultResponse.ok().setData(idempotent);}
-> 2.3.6 接口测试
ps: 使用方式: AxPost可以换成任意实体类
ResultResponse 随便的一个统一返回类型,没有写String 类型即可
@ApiIdempotentAnn@PostMapping("/testCheck")public ResultResponse testCheck(@RequestBody AxPost axPosts) {log.info("======>专业测试幂等性接口 <=======");System.out.println(axPosts);return ResultResponse.ok("ok");}
-> 2.3.7 使用测试工具进行测试
测试工具介绍: Postman,ApiPost, Idea httpclient tools,ApiFox后端测试方式
-> 2.3.8 幂等校验token的优,缺点
-----> 2.3.8.1 使用的优点:
通过token机制来保证幂等是常见的解决方案,同时也适合绝大部分场景,使用方便,
业务执行出现异常时,客户端需重新获取令牌并发起访问即可。推荐使用先删除token方案。
-----> 2.3.8.2 使用的缺点:
每次的业务请求, 都会产生一个额外的请求去获取token,但出现失败次数一般不会很多, 算是一种资源的浪费,虽然redis性能很好.
3. 前端如何操作来避免幂等问题
-> 3.1前端防重
通过前端防重保证幂等是最简单的实现方式,JS代码即可完成设置。可靠性并不好,可以通过工具跳过页面仍能重复提交。主要适用于表单重复提交或按钮重复点击。
-> 3.2 PRG模式
PRG模式即POST-REDIRECT-GET。当用户进行表单提交时,会重定向到另外一个提交成功页面,而不是停留在原先的表单页面。这样就避免了用户刷新导致重复提交。防止通过浏览器按钮前进/后退导致表单重复提交。
相关文章:
01-幂等性解释,问题及常用解决方案
目录 1. 幂等性简介 2. 后端如何解决幂等性问题 2.1 数据库层面 -> 2.1.1 防重表 -> 2.1.2 数据库悲观锁(不建议,容易出现死锁情况) -> 2.1.3 数据库乐观锁 -> 2.1.4 乐观锁CAS算法原理 2.2 锁层面 2.3 幂等性token层面 -> 2.3.1 简介文字描述: …...
SpringBoot配置文件
配置文件有两种格式: .properties .yml .properties是老版配置文件,.yml是新版配置文件 一、properties详解 IDEA社区版不支持 properties格式的日志的提示,需要安装相应插件。 3.1properties 基本语法 (ps:小技巧࿰…...
基于蜣螂算法改进的DELM分类-附代码
蜣螂算法改进的深度极限学习机DELM的分类 文章目录蜣螂算法改进的深度极限学习机DELM的分类1.ELM原理2.深度极限学习机(DELM)原理3.蜣螂算法4.蜣螂算法改进DELM5.实验结果6.参考文献7.Matlab代码1.ELM原理 ELM基础原理请参考:https://blog.c…...
FPGA纯verilog代码实现图像对数变换,提供工程源码和技术支持
目录1、图像对数变换理论2、log系数的matlab生成3、FPGA实现图像对数变换4、vivado与matlab联合仿真5、vivado工程介绍6、上板调试验证并演示7、福利:工程代码的获取1、图像对数变换理论 对数变换可以将图像的低灰度值部分扩展,显示出低灰度部分更多的细…...
【Python百日进阶-Web开发-Vue3】Day516 - Vue+ts后台项目3:首页
文章目录 一、首页头部1.1 element-plus中找到适合的Container布局容器1.2 头部容器Layout 布局1.3 src/views/HomeView.vue二、侧边菜单栏2.1 element-plus中找到适合的Menu侧栏2.2 src/views/HomeView.vue三、侧边栏的动态路由3.1 src/views/HomeView.vue3.2 src/views/Goods…...
分析了 200 个 DeFi 项目,我发现了这些规律
作者:Ren & Heinrich翻译:dongdong在这篇文章中,我分享了我通过分析当前排名前 200 的 DeFi 加密项目的见解。这不是一项学术研究。尽管如此,这些发现对加密货币投资者来说具有附加值。我使用 https://defillama.com/ 的公共数…...
你领证了吗?各地2022下半年软考纸质证书发放中
不少同学都在关注2022下半年软考证书领取时间,截止至目前,江苏、南京、山东、浙江、贵州、云南、大连、广西地区的纸质证书可以领取了,请大家在证书申领时间内及时预约证书邮寄发放哦~ 江苏 证书领取时间:2023年2月3日起 南京 …...
将群晖NAS变为本地盘
本文介绍一个工具,可以在 Windows 系统下将群晖NAS的目录变为本地盘,好处是在外部访问的时候,能够大大改善体验。可以用本地的应用程序直接打开,速度依赖网络带宽,正常情况下,看视频是没有问题的。当然&…...
以太坊上交易异常Pending的处理方法
交易Pending ETH交易pending的原因: 1.交易GasPrice设置过低,共识节点不打包 2.账户Nonce不连续,一直处于交易池队列当中 只要确认了是哪种原因引起的,就可以做出对应的解决方案。 GasPrice设置过低 由于ETH共识节点是按照Gas价格从高到低打包交易,如果每笔交易的GasPr…...
第三节 第一个内核模块
hellomodule 实验 实验说明 硬件介绍 本节实验使用到STM32MP157 开发板 实验代码讲解 本章的示例代码目录为:linux_driver/module/hellomodule 从前面我们已经知道了内核模块的工作原理,这一小节就开始写代码了,跟hello world 一样&…...
从CNN到Transformer:基于PyTorch的遥感影像、无人机影像的地物分类、目标检测、语义分割和点云分类
我国高分辨率对地观测系统重大专项已全面启动,高空间、高光谱、高时间分辨率和宽地面覆盖于一体的全球天空地一体化立体对地观测网逐步形成,将成为保障国家安全的基础性和战略性资源。随着小卫星星座的普及,对地观测已具备多次以上的全球覆盖…...
操作系统的奋斗(三)内存管理
第三章 内存管理3.1内存管理概念3.1.1 内存管理的基本原理和要求(1)内存管理的主要功能3.1.2 覆盖和交换(1)覆盖(2)交换3.1.3 连续分配管理方式(1)单一连续分配(2&#x…...
多选多的一种通用处理逻辑
开发的时候,我们经常会涉及元素的多选多,并且还需要对选中的元素进行拖动排序 通用的设计方案如下 游戏资源集合与游戏资源的绑定关系处理(多选多的一种通用处理逻辑) 可能的情况: 1.之前被选中的资源,现…...
Redis 的安装 + SpringBoot 集成 Redis
1.安装 Redis此处的 Redis 安装是针对 Linux 版本的安装, 因为 Redis 官方没有提供 Windows 版本, 只提供了 Linux 版本. 但是我们可以通过Windows 去远程连接 Redis.1.1 使用 yum 安装 Redis使用如下命令, 将 Redis 安装到 Linux 服务器:yum -y install redis1.2 启动 Redis使…...
为什么在容器中 1 号进程挂不上 arthas?
作者:卜比 本文是《容器中的 Java》系列文章之 4/n ,欢迎关注后续连载 😃 。 系列1:JVM 如何获取当前容器的资源限制? 系列2:Java Agent 踩坑之 appendToSystemClassLoaderSearch 问题 系列3:让…...
23种设计模式之策略模式
一、概念 就是将一系列算法封装起来,并使它们之间相互替换。被封装起来的算法具有独立性外部不可改变其特性。 策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算…...
不会做UI自动化测试?一起设计框架再实践吧
目的相信做过测试的同学都听说过自动化测试,而UI自动化无论何时对测试来说都是比较吸引人的存在。相较于接口自动化来说它可以最大程度的模拟真实用户的日常操作与特定业务场景的模拟,那么存在即合理,自动化UI测试自然也是广大测试同学职业道…...
数据分析实战项目3:RFM用户分群
目录1、RFM模型介绍2、Excel实际RFM划分案例3、RFM案例3.1 数据加载和基本信息查看3.2 数据预处理和RFM的初始值计算3.3 RFM区间和划分和分值计算3.4 RFM计算结果保存3.4.1 保存到excel3.4.2 保存到数据库3.5 RFM计算结果可视化3.6 结果分析(营销建议)3.…...
消息中间件概述
目录1.为什么学习消息队列2.什么是消息中间件3.消息队列应用场景3.1 应用解耦3.2 异步处理3.3 流量削峰3.4 什么是QPS,PV3.5 什么是PV,UV,PR4. AMQP 和 JMS4.1 AMQP4.2 JMS4.3. AMQP 与 JMS 区别5. 消息队列产品6. RabbitMQ6.1 RabbitMQ简介6.2 RabbitMQ 中的相关概…...
vue和js给后端接口返回的数据(如以json数据为元素的数组)添加新的json字段
文章目录vue和js给后端接口返回的数据(如以json数据为元素的数组)添加新的json字段1. res为后端接口的响应2. 获取后端接口返回的数据3. 向 tableData 添加字段3.1. 向 tableData 中添加一个新json元素( {"time", "2023-02-09"} )3.…...
负载均衡的方式
在业务初期,我们一般会先使用单台服务器对外提供服务。随着业务流量越来越大,单台服务器无论如何优化,无论采用多好的硬件,总会有性能天花板,当单服务器的性能无法满足业务需求时,就需要把多台服务器组成集…...
python(15)--函数设计
前言 函数是可重用的程序代码块。 函数的作用,不仅可以实现代码的复用,还可以保证修改函数的代码时,所有调用该函数的地方都能得到体现。目前我已知函数的作用是:对代码实现了封装、函数调用、传递参数、返回计算结果等。 正文 …...
手把手教你用Python做可视化数据,还能调节动画丝滑度
数据可视化动画还在用Excel做? 现在一个简单的Python包就能分分钟搞定! 而且生成的动画也足够丝滑,效果是酱紫的: 这是一位专攻Python语言的程序员开发的安装包,名叫Pynimate。 目前可以直接通过PyPI安装使用。 使用…...
湖南中创教育PMP项目管理——变更管理
【变更管理】包括 一、如何理解需求变更 二、如何控制需求变更 三、项目变更管理流程 四、如何应对“奇葩”变更 一、如何理解需求变更: 1、项目中发生变更是正常现象,变更无法回避 2、哪里都可能出现变更,任何人都有权提出变更 3、…...
IC真题 —— 刷题记录(1)
引言 记录一些 我自己刷的 IC行业招聘真题,不是每题记录,只记录一些值得记录的,写下自己的看法。主要是一些数字IC行业题目,偏前端。 1、有一个逐次逼近型 8位A/D 转换器,若时钟频率为250KHz,完成一次转换…...
【C++入门】命名空间,输出输入,缺省参数,函数重载
文章目录命名空间C输入与输出缺省参数函数重载命名空间 在C/C中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标 识符的名称进行本地化࿰…...
cmu 445 poject 2笔记
2022年的任务 https://15445.courses.cs.cmu.edu/fall2022/project2/ checkpoint 1,实现b树,读,写,删 checkpoint 2, 实现b树,迭代器,并发读写删 本文不写代码,只记录遇到的一些思维盲点 checkp…...
梅开二度的 axios 源码阅读,三千字详细分享功能函数,帮助扩展开发思维
前言 第一遍看 axios 源码,更多的是带着日常开发的习惯,时不时产生出点联想。 第二遍再看 axios 源码,目标明确,就是奔着函数来的。 当有了明确清晰的目标,阅读速度上来了,思绪也转的飞快。 按图索骥&a…...
vcs仿真教程
VCS是在linux下面用来进行仿真看波形的工具,类似于windows下面的modelsim以及questasim等工具,以及quartus、vivado仿真的操作。 1.vcs的基本指令 vcs的常见指令后缀 sim常见指令 2.使用vcs的实例 采用的是全加器的官方教程,首先介绍不使用…...
java 自定义json解析注解 复杂json解析 工具类
java 自定义json解析注解 复杂json解析 工具类 目录java 自定义json解析注解 复杂json解析 工具类1.背景2、需求-各式各样的json一、一星难度json【json对象中不分层】二、二星难度json【json对象中出现层级】三、三星难度json【json对象中存在数组】四、四星难度json【json对象…...
广州市花都区网站建设公司/百度关键词搜索量查询
将Vim改造为强大的IDE—Vim集成Ctags/Taglist/Cscope/Winmanager/NERDTree/OmniCppComplete1、安装Vim和Vim基本插件 首先安装好Vim和Vim的基本插件。这些使用apt-get安装即可: lingdubuntu:~/arm$sudo apt-get install vim vim-scripts vim-doc 其中vim-scripts是v…...
广州城市建设网站/软文广告图片
可空的引用类型(Nullable Reference Types) 可空的引用类型可概括地表述为,引用类型将不再默认可空。因此,开发人员必须使用定义可空值类型的同样语法“Type?”,显式地标记一个引用类型为可空。 如果将一个空值赋值给…...
佳木斯市郊区建设局网站/我要推广网
本期我们继续进行运筹学之图与网络分析算法的讲解,我们将对图与网络分析的基础知识进行一个简单的回顾,并介绍求解最大流问题和最小费用最大流的MATLAB和Python相关代码,以帮助大家利用工具快速求解最大流问题和最小费用最大流问题࿰…...
怎么做时时彩彩票网站/搜狗站长管理平台
公众号关注 “GitHubDaily”设为 “星标”,每天带你逛 GitHub!集成开发环境(IDE,Integrated Development Environment )是用于提供程序开发环境的应用程序,不管是 Java、C 还是 Python,使用 IDE 编程可以帮你检查语法、…...
国内外优秀网站设计/百度推广的广告真实可信吗
对于每一位开发者而言,软件开发工具的重要性显然不言而喻。事实上,除了个人能力之外,它们也在团队的效率及实际生产力层面扮演着关键性角色。 在今天的文章中,我们整理出17款软件开发工具,如果您尚未尝试过其中部分工具…...
kaalus wordpress/竞价交易
HTTP Client - IntelliJ IDEs Plugin | Marketplace...