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.…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...