问题:幂等性 分布式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) //令牌验证失败
           

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/10635.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux下安装mysql8.0(以tar.xz包安装--编译安装)

前言&#xff1a;原文在我的博客网站中&#xff0c;持续更新数通、系统方面的知识&#xff0c;欢迎来访&#xff01; Linux下安装mysql8.0&#xff08;以tar.xz包安装--编译安装&#xff09;https://myweb.myskillstree.cn/126.html 目录 一、下载对应自己glic版本的MySQL …

1.基于python的单细胞数据预处理-降维可视化

目录 降维的背景PCAt-sneUMAP检查质量控制中的指标 参考&#xff1a; [1] https://github.com/Starlitnightly/single_cell_tutorial [2] https://github.com/theislab/single-cell-best-practices 降维的背景 虽然特征选择已经减少了维数&#xff0c;但为了可视化&#xff0…

C++语法|智能指针的实现及智能指针的浅拷贝问题、auto_ptr、scoped_ptr、unique_ptr、shared_ptr和weak_ptr详细解读

文章目录 1.自己实现智能指针智能指针引起的浅拷贝问题尝试定义自己的拷贝构造函数解决浅拷贝 2.不带引用计数的智能指针auto_ptrscoped_ptrunique_ptr&#xff08;推荐&#xff09; 3.带引用计数的智能指针模拟实现引用计数shared_ptr和weak_ptr循环引用&#xff08;交叉引用&…

DDD架构理论详解

文章目录 一、概念入门1. 概念简介2. DDD的核心理念3. 范式4. 模型5. 框架6. 方法论7. 软件设计的主要活动 二、DDD核心理论1. Domain领域层都包含什么&#xff1f;2. 聚合、实体和值对象3. 仓储&#xff0c;封装持久化数据4. 适配&#xff08;端口&#xff09;&#xff0c;调用…

AI应用案例:新闻文本分类

随着科学技术的不断发展&#xff0c;互联网技术得以快速的发展和普及&#xff0c;并已在各行各业得到了广泛的应用&#xff0c;从中致使了网络上的信息呈现出爆炸式的增长状态&#xff0c;达到了“足不出户&#xff0c;万事皆知”的境况&#xff0c;充分体现了互联网新闻给生活…

Java实现自定义注解,实现不需要token 验证就可以访问接口

目录 1 问题2 实现 1 问题 一个springboot 项目&#xff0c;需要token 验证&#xff0c;前端传过来token ,我们一般在项目全局写一个过滤器&#xff0c;去验证前端传过来的token ,如果有哪些接口不需要token验证&#xff0c;那么就排除这些接口&#xff0c;这个也需要配置。 …

前端 | TED打卡号分类查询

文章目录 &#x1f4da;实现效果&#x1f4da;模块实现解析&#x1f407;html&#x1f407;css&#x1f407;javascript &#x1f4da;实现效果 提供完整TED打卡号对应TED标题的查询列表 根据分类按需查询 &#x1f4da;模块实现解析 &#x1f407;html 搭框架<div cl…

Android Studio连接MySQL8.0

【序言】 移动平台这个课程要做一个app的课设&#xff0c;我打算后期增加功能改成毕设&#xff0c;就想要使用MySQL来作为数据库&#xff0c;相对于SQLlite来说&#xff0c;我更熟悉MySQL一点。 【遇到的问题】 一直无法连接上数据库&#xff0c;开始的时候查了很多资料&#…

海外云手机解决海外社交媒体运营难题

随着全球数字化浪潮的推进&#xff0c;海外社交媒体已成为外贸企业拓展市场、提升品牌影响力的重要阵地。Tiktok、Facebook、领英、twitter等平台以其庞大的用户基础和高度互动性&#xff0c;为企业提供了前所未有的营销机会。本文将介绍如何通过海外云手机&#xff0c;高效、快…

eNSP中小型园区网络拓扑搭建(下)

→b站直通车&#xff0c;感谢大佬← →eNSP中小型园区网络拓扑搭建&#xff08;上&#xff09;← 不带配置命令的拓扑图已上传~ 配置ospf SW5 # ospf 1 router-id 5.5.5.5area 0.0.0.0network 192.168.51.5 0.0.0.0network 192.168.52.5 0.0.0.0area 0.0.0.10network 192.1…

elk + filebeat 8.4.3 收集nginx日志(docker部署)

ELK filebeat docker部署 一、 elasticsearch部署1、运行elasticsearch临时配置容器2、拷贝文件目录到本地3、检查elasticsearch.yml4、删除之前elastic&#xff0c;运行正式容器5、docker logs记录启动日志 二、部署kibana1、运行kibana临时配置容器2、docker拷贝配置文件到本…

数据链路层——计算机网络学习笔记三

使用点对点信道的数据链路层 前言&#xff1a; 1.数据链路层的重要性&#xff1a;网络中的主机、路由器都必须实现数据连输层&#xff1b; 2.数据链路层中使用的信道&#xff1a; 点对点信道&#xff1a;这种信道是一对一的通信方式&#xff1b; 广播信道&#xff1a;使用一对多…

硬盘架构原理及其算法RAID工作原理写惩罚

一、硬盘的架构以及寻址原理 硬盘工作原理&#xff1a; 硬盘寻址原理&#xff1a;逻辑顺序磁道、盘片、扇区&#xff08;顺序CHS&#xff09; 二、机械硬盘算法 读取算法 寻道算法 个人与企业适合的算法和寻道 个人使用的机械硬盘适合的寻道算法和读取算法是&#xff1a…

WPS表格:使用vlookup函数解决乱序数据对应问题

我们常常会遇到两个表格的内容相同&#xff0c;但是顺序不一致的情况。并且这种顺序无关于简单的排序&#xff0c;而是一种业务性很强的复杂排序规则。下面我举个例子&#xff0c;使用VLOOKUP复制数据。 假设太阳系行星举办了一次卖萌比赛&#xff0c;由太阳妈妈决定谁是最萌的…

ElasticSearch 8.X 源码导入idea并配置环境启动调试(mac环境)

主要是用于自己记录配置流程 环境 IntelliJ IDEA 2024.1.1 (Community Edition) jdk17&#xff08;可以安装jenv管理&#xff09; macos 14.4.1 gradle 8.5 资源准备 先在官网下载elasticsearch源码&#xff08;GitHub - elastic/elasticsearch: Free and Open, Distrib…

GeoServer安装以及部署

GeoServer介绍 GeoServer是一个开源的服务器软件&#xff0c;用于共享和编辑地理空间数据。它支持多种地理空间数据格式&#xff0c;并且可以发布为多种服务格式&#xff0c;如Web Feature Service (WFS)、Web Map Service (WMS)、Web Coverage Service (WCS)&#xff0c;以及…

SeetaFace6人脸特征提取与对比C++代码实现Demo

SeetaFace6包含人脸识别的基本能力&#xff1a;人脸检测、关键点定位、人脸识别&#xff0c;同时增加了活体检测、质量评估、年龄性别估计&#xff0c;并且顺应实际应用需求&#xff0c;开放口罩检测以及口罩佩戴场景下的人脸识别模型。 官网地址&#xff1a;https://github.co…

pyqt颜色变换动画效果

pyqt颜色变换动画效果 QPropertyAnimation介绍颜色变换效果代码 QPropertyAnimation介绍 QPropertyAnimation 是 PyQt中的一个类&#xff0c;它用于对 Qt 对象的属性进行动画处理。通过使用 QPropertyAnimation&#xff0c;你可以平滑地改变一个对象的属性值&#xff0c;例如窗…

Python-VBA函数之旅-str函数

目录 一、str函数的常见应用场景 二、str函数使用注意事项 三、如何用好str函数&#xff1f; 1、str函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a; https://myelsa1024.blog.csdn.net/ 一、str函数的常…

iphone进入恢复模式怎么退出?分享2种退出办法!

iPhone手机莫名其妙的进入到了恢复模式&#xff0c;或者是某些原因需要手机进入恢复模式&#xff0c;但是之后我们不知道如何退出恢复模式怎么办&#xff1f; 通常iPhone进入恢复模式的常见原因主要是软件问题、系统升级失败、误操作问题等导致。那iphone进入恢复模式怎么退出&…