问题:幂等性 分布式session
web项目中请求线程到service层的时候远程调用服务之前是串行化执行每个任务都要get阻塞等待任务完成,举例当用户在购物车页面点击去结算就会请求后台toTrade请求获取订单确认的详情数据并渲染到订单详情页,现在在toTrade请求中使用异步任务编排CompletableFuture.runAsync(来使用线程池并提交任务,这导致远程请求时需要利用当前的请求线程中放在ThreadLocal的共享数据无法在CompletableFuture.runAsync提交的异步任务中获取到主请求线程的共享数据,如请求会员服务获取用户的地址,请求购物车服务获取选中的购物车项,在远程feign调用的时候需要实现拦截器为新的request设置上cookie,在拦截器中要获取到旧的request,这时不在同一个线程获取到的request就是null,理论的当前线程中 RequestContextHolder.getRequestAttributes();会保存controller中接收的request原理是ThreadLocal共享变量,现在解决方法:在异步线程编排前获取到 RequestAttributes requestAttributes= RequestContextHolder.getRequestAttributes();在异步编排的任务中
为当前线程的RequestContextHolder重新设置上原来的请求数据,让每一个线程都来共享之前的请求数据RequestContextHolder.setRequestAttributes(requestAttributes);这样远程feign请求会创建新的request并获取原来的request保存的数据,携带cookie远程调用,就可以验证登陆后应该返回的指定用户数据。
幂等性问题解决
使用token:使用方式和验证码相同,有服务器存储一份,前端发送一份,订单提交时携带token验证通过后删除token并创建订单,当多次提交token无法验证通过。token设及的问题有先删除token还是后删除token问题,后删除token可能重复提交后创建相同的订单,破坏幂等,如果是先删除token,在微服务中token不可能单独存储在一个服务中,所以它和分布式session一样需要储存在redis中,当多个服务订单携带token时需要竞争获取服务端的token,服务端需要从redis中拿,当两个服务同时从redis中拿token成功,同时删除令牌,同时创建订单,幂等性失效。
解决方式:获取getToken判断相等getToken==token 删除delToken这三个要是原子性的保证只有一个服务执行完整操作。可以在redis的lua脚本完成操作
if redis.call("get",KYES[1]==ARGV[1] return redis.call("del".KEYS[2])else return 0 end)
2 解决方式使用各种锁机制
1数据库悲观锁
select * from xx where id=1 for update(行锁) 悲观锁使用一般随事务一起使用,数据锁定时间可能很长,需要更具实际情况选用,id字段一定是主键或者唯一索引,不然可能造成锁表的结果,处理起来会麻烦。
数据库乐观锁
数据库更新的时候加上version字段(比如订单下单成功减库存操作可能feign触发重试机制)
3 业务层使用分布式锁
各种唯一约束
1数据可唯一约束 主键唯一索引举例下订单的订单号是唯一的索引,重复下订单只有一次是有效的
2 使用redis set防重,拿百度网盘的妙功能,如果这个文件之前有人上传过就计算它的MD5值,放在set中,每一个独立存在的文件只有一个MD5值,拿订单为例,如果这个订单得到处理了,就生成一个该订单好号的MD5值,如果重复提交就检测set中是否有该订单的MD5值如果有表明订单已经处理成功,重复提交就失败。
3 防重表
使用订单号 orderNo 做为去重表的唯一索引,把唯一索引插入去重表,再进行业务操作,且 他们在同一个事务中。这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,避 免了幂等问题。这里要注意的是,去重表和业务表应该在同一库中,这样就保证了在同一个 事务,即使业务操作失败了,也会把去重表的数据回滚。这个很好的保证了数据一致性。 之前说的 redis 防重也算。
4、全局请求唯一 id
调用接口时,生成一个唯一 id,redis 将数据保存到集合中(去重),存在即处理过。 可以使用 nginx 设置每一个请求的唯一 id; proxy_set_header X-Request-Id $request_id;
最终实现
本项目通过token解决幂等性,在订单确认页面的请求中返回一个token给前端,并在后端统一存储token到redis中,由于是分布式系统,并不像传统单点服务一样保存在当前服务中,而是通过原子性获取redis的token确保每个竞争token的服务在获取比较删除都是原子性的来保证多个服务不相互干扰引发数据混乱。下订单验证token的lua脚本实现如下:
@Overridepublic SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {confirmVoThreadLocal.set(vo);SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();//去创建、下订单、验令牌、验价格、锁定库存...//获取当前用户登录的信息MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();responseVo.setCode(0);//1、验证令牌是否合法【令牌的对比和删除必须保证原子性】String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";String orderToken = vo.getOrderToken();//通过lure脚本原子验证令牌和删除令牌Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),Arrays.asList(USER_ORDER_TOKEN_PREFIX + memberResponseVo.getId()),orderToken);if (result == 0L) {//令牌验证失败responseVo.setCode(1);return responseVo;} else {//令牌验证成功//1、创建订单、订单项等信息OrderCreateTo order = createOrder();//2、验证价格BigDecimal payAmount = order.getOrder().getPayAmount();BigDecimal payPrice = vo.getPayPrice();if (Math.abs(payAmount.subtract(payPrice).doubleValue()) < 0.01) {//金额对比//TODO 3、保存订单saveOrder(order);//4、库存锁定,只要有异常,回滚订单数据//订单号、所有订单项信息(skuId,skuNum,skuName)WareSkuLockVo lockVo = new WareSkuLockVo();lockVo.setOrderSn(order.getOrder().getOrderSn());//获取出要锁定的商品数据信息List<OrderItemVo> orderItemVos = order.getOrderItems().stream().map((item) -> {OrderItemVo orderItemVo = new OrderItemVo();orderItemVo.setSkuId(item.getSkuId());orderItemVo.setCount(item.getSkuQuantity());orderItemVo.setTitle(item.getSkuName());return orderItemVo;}).collect(Collectors.toList());lockVo.setLocks(orderItemVos);//TODO 调用远程锁定库存的方法//出现的问题:扣减库存成功了,但是由于网络原因超时,出现异常,导致订单事务回滚,库存事务不回滚(解决方案:seata)//为了保证高并发,不推荐使用seata,因为是加锁,并行化,提升不了效率,可以发消息给库存服务R r = wmsFeignService.orderLockStock(lockVo);if (r.getCode() == 0) {//锁定成功responseVo.setOrder(order.getOrder());// int i = 10/0;//TODO 订单创建成功,发送消息给MQrabbitTemplate.convertAndSend("order-event-exchange","order.create.order",order.getOrder());//删除购物车里的数据redisTemplate.delete(CART_PREFIX+memberResponseVo.getId());return responseVo;} else {//锁定失败String msg = (String) r.get("msg");throw new NoStockException(msg);// responseVo.setCode(3);// return responseVo;}} else {responseVo.setCode(2);return responseVo;}}}
大致内容
//1、验证令牌是否合法【令牌的对比和删除必须保证原子性】
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
String orderToken = vo.getOrderToken();
//通过lure脚本原子验证令牌和删除令牌
Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
Arrays.asList(USER_ORDER_TOKEN_PREFIX + memberResponseVo.getId()),
orderToken);
if (result == 0L) //令牌验证失败
相关文章:

问题:幂等性 分布式session
web项目中请求线程到service层的时候远程调用服务之前是串行化执行每个任务都要get阻塞等待任务完成,举例当用户在购物车页面点击去结算就会请求后台toTrade请求获取订单确认的详情数据并渲染到订单详情页,现在在toTrade请求中使用异步任务编排Completab…...

Golang | Leetcode Golang题解之第66题加一
题目: 题解: func plusOne(digits []int) []int {n : len(digits)for i : n - 1; i > 0; i-- {if digits[i] ! 9 {digits[i]for j : i 1; j < n; j {digits[j] 0}return digits}}// digits 中所有的元素均为 9digits make([]int, n1)digits[0]…...

c++ STL 之栈—— stack 详解
vector 是 stl 的一个关联容器,名叫“栈”,何为“栈”?其实就是一个数组,但有了数组何必还需栈,这是一个高深的问题。 一、简介 1. 定义 栈,是一个柔性数组(可变长数组),可以变大变小…...

鸿蒙开发接口Ability框架:【(窗口扩展能力)】
窗口扩展能力 WindowExtensionAbility基于ExtensionAbility,WindowExtensionAbility中展示的内容作为一个控件(AbilityComponent)内容展示在其他应用窗口中,实现在一个窗口中展示多个应用程序内容的功能。 说明: 本模块首批接口从API versio…...

AutoCAD中密集的填充打散后消失的问题
有时候在AutoCAD中,图案填充的填充面积过大或填充太过密集时,将该填充打散,也就是执行Explode时,会发现填充图案消失了。 原因是打散后线条太大,系统就不显示了。可以通过设置:HPMAXLINES 值,来…...

基于Matplotlib的模型性能可视化工作
一、项目简介 本项目是科技考古墓葬识别工作的中间过程,因为需要大量复用所以另起一章好了。 主要涉及到数据读取、数据可视化和少量的数据处理过程。 二、相关知识 PandasMatplotlib 三、实验过程 1. 数据探索性分析 1.1 准备工作–导入模块 import pandas…...

KAN网络最全解析——比肩MLP和Transformer?
1 基本思路 1.1 MLP与Spline的优缺点 多层感知器 (MLP)是深度学习的基础理论模块,是目前可用于逼近非线性函数的默认模型,其表征能力已由通用逼近定理证明。但MLP也有明显的缺点,例如在 Transformer中,MLP 的参数量巨大…...

ASP.NET学生信息管理系统
摘 要 本文介绍了在ASP.net环境下采用“自上而下地总体规划,自下而上地应用开发”的策略开发一个管理信息系统的过程。通过分析某一学校学生管理的不足,创建了一套行之有效的计算机管理学生的方案。文章介绍了学生管理信息系统的系统分析部分,…...

图片改大小尺寸怎么改?几招教你搞定图片修改
在社交媒体平台上发布图片时,调整图片的尺寸大小可以确保图片适合平台的要求,不同的社交媒体平台可能对图片的尺寸有不同的要求,通过调整图片尺寸,可以更加完美的展现出来,那么有没有比较简单的图片改大小的方法呢&…...

Scala编程入门:从零开始的完整教程
目录 引言环境准备创建第一个Scala项目基本语法高阶概念进阶资源结语 引言 Scala是一种强大的、静态类型的、多范式编程语言,它结合了面向对象和函数式编程的特点。本教程将指导您如何从零开始学习Scala,并搭建一个简单的开发环境。让我们开始探索Scala…...

Proxmox VE 8 SDN创建VLAN隔离用户网络
作者:田逸(formyz) 在上一篇文章中,我们用SDN的Simple对租户(用户)网络实现了隔离功能,但它有个限制,仅仅能在单个物理节点上进行通信,而不能跨越物理节点(除…...

API低代码平台介绍3-异构数据源的数据查询功能
异构数据源的数据查询功能 在上一篇文章中我们通过API平台定义了一个最基本的数据查询接口,本篇文章我们将上升难度,在原有接口的基础上,实现在MySQL数据库和Oracle数据库同时进行数据查询。 什么场景会需要同时对异构数据源进行查询&…...

【Linux】-网络请求和下载、端口[6]
目录 一、网络请求和下载 1、ping命令 2、wget命令 3、curl命令 二、端口 1、虚拟端口 2、查看端口占用 一、网络请求和下载 1、ping命令 可以通过ping命令,检查指定的网络服务器是否可联通状态 语法:ping [ -c num ] ip或主机名 选项&…...

Github2024-05-10开日报 Top10
根据Github Trendings的统计,今日(2024-05-10统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目4TypeScript项目4JavaScript项目1Lua项目1C项目1Rust项目1Dart项目1 RustDesk: 用Rust编写的开源远…...

2016-2021年全国范围的2.5m分辨率的建筑屋顶数据
一、论文介绍 摘要:大规模且多年的建筑屋顶面积(BRA)地图对于解决政策决策和可持续发展至关重要。此外,作为人类活动的细粒度指标,BRA可以为城市规划和能源模型提供帮助,为人类福祉带来好处。然而…...

Gitea 上传用户签名
在 Gitea 的用户管理部分,有一个 SSH 和 GPG 的选项。 单击这个选项,可以在选项上添加 Key。 Key 的来源 如是 Windows 的用户,可以选择 Kleopatra 这个软件。 通过这个软件生成的 Key 的界面中有一个导出功能。 单击这个导出,…...

【原创】springboot+mysql物资库存管理系统设计与实现
个人主页:程序猿小小杨 个人简介:从事开发多年,Java、Php、Python、前端开发均有涉猎 博客内容:Java项目实战、项目演示、技术分享 文末有作者名片,希望和大家一起共同进步,你只管努力,剩下的交…...

vulnhub靶场之FunBox-5
一.环境搭建 1.靶场描述 Lets separate the script-kids from script-teenies.Hint: The first impression is not always the right one!If you need hints, call me on twitter: 0815R2d2 Have fun...This works better with VirtualBox rather than VMwareThis works bett…...

1分钟搞定Pandas DataFrame创建与索引
1.DataFrame介绍 DataFrame 是一个【表格型】的数据结构,可以看作是【由Series组成的字典】(共用同一个索引)。DataFrame 由按一定顺序排列的多列数据组成。设计初衷是将 Series 的使用场景从一维扩展到多维。DataFrame 既有行索引ÿ…...

【贪心算法】哈夫曼编码Python实现
文章目录 [toc]哈夫曼编码不同编码方式对比前缀码构造哈夫曼编码哈夫曼算法的正确性贪心选择性质证明 最优子结构性质证明 总结 Python实现时间复杂性 哈夫曼编码 哈夫曼编码是广泛用于数据文件压缩的十分有效的编码方法,其压缩率通常为 20 % 20\% 20%到 90 % 90\%…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...

毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...