Redisson分布式锁实战
实战来源
此问题基于电商
这周遇见这么一个问题,简略的说一下
由
MQ发布了两个消息,一个是订单新增,一个是订单状态变更由于直接付款之后,这两个消息的发布时间不分先后,可能会造成两种情况,1、订单状态变更在订单新增之前;2、订单新增在订单状态变更之前
逻辑二没有问题,有问题的是逻辑一,如果订单状态变更在订单新增之前,那么连新建订单都没有入库,怎么能改变订单的状态呢
为了完成这个逻辑就需要使用锁来让两个业务同步,必须让新建订单在订单状态变更之前,我这里使用的是基于
Redisson的分布式Redis锁
前提,需要了解Redisson分布式锁、redis
redisson:Redis实现分布式锁原理和Redisson框架实现分布式锁,全网最详细讲解_
redis工具类:Redis工具类(redisTemplate)以及 redisTemplate 的用法
代码如下,从MQ取消息的的逻辑我就不写了
这里的代码还是有问题的,但在生产中95%可能遇不见,要么服务器炸了,要么redis挂了,要么其他人的接口被人发现漏洞随意调用,导致MQ传递参数有误,那么都是别人的问题,跟我们没关系
因为同一订单,订单新建和订单状态变更的订单号就是一样的,我用
Redis做了以下三个K-V键值对1、redis的key=
"前缀_STATUS_SYNCHRONIZATION_" + json.getString("订单号")这个是,
redis锁的key,保证新建订单和订单状态变更的主要逻辑能同步,value由redisson生成我们不用管,只用管超时时间就好了2、redis的key=
"前缀_NEW_ORDER_" + json.getString("订单号")这个是,判断是否新建订单在订单状态变更之前,有下述两种情况
- 如果新建订单在订单状态变更之前,那么新建订单时会存入这个2中的
redis的K-V值,在订单状态变更的时候会用redis访问2中的key发现不为空(redis存在2中的K-V关系),就直接走入库操作- 如果订单状态变更在新建之前,访问
redis2的这个key发现为空(redis不存在2中的K-V关系),就用下述3中的K-V把需要入库的订单状态变更的实体类存起来,由新建订单时候调用时。新建订单时发现下述3中的K-V存在就说明订单状态变更在新建订单之前,就需要把下述3中的V取出来做入库处理,否则就认为新建订单在订单状态变更之前,就不需要做处理
value无所谓是什么,不重要,我这里就写的value=等待付款没什么特殊含义。设置了两个小时的超时时间,因为订单超过两个小时了就不让付款了3、redis的key=
"前缀_STATUS_PUSH_" + json.getString("订单号")这个是,如果订单状态变更在新建订单之前,那么我就存进去,由新建的时候判断是否存在这个值,如果存在就说明订单在状态变更之前,就取出这个
key对应的value(因为我们存的就是订单状态变更时需要的数据)做入库处理,不存在说明新建订单在订单状态变更之前就不需要做处理
/*** 我的redis工具类,在前提中有*/@Autowiredprivate RedisUtil redisUtil;/*** redisson,只需要配置一个配置类就可以使用了,可以看前提中redisson的文章*/@Resourceprivate Redisson redisson;/*** 订单状态变更* @param json* @return*/public boolean orderStatusChange(JSONObject json){try {// ...// 上面拼接需要入库的逻辑得到订单状态变更的实体类信息,不可能把源码展示出来,我仅放关键代码,order 就相当于从 MQ 获取的内容封装成了实体类 orderOrder order = new Order();// 主要看下面的代码// 只有付款的时候走此逻辑,因为可能出现,直接付款,状态推送和新建订单一起发过来且付款先执行的操作if (json.getString("code").equals("付款的code")){// 如果已付款是先执行就先缓存由新建调用RLock lock = redisson.getLock("前缀_STATUS_SYNCHRONIZATION_" + json.getString("订单号"));// 60秒自旋拿锁,拿到锁之后持有60秒 (如果后面的值 <0 的话会使用开门狗机制,一直持有锁,除非项目挂掉了)boolean success = lock.tryLock(60L, 60L, TimeUnit.SECONDS);try {// 判断新建订单执行了吗,因为我的逻辑是如果新建订单执行后会执行下述注解中的代码,存一个两小时的 redis 缓存,因为新建订单之后两个小时不付款就取消订单了// 这是新建订单后设置的值: redisUtil.set("前缀_NEW_ORDER_" + json.getString("订单号"),"已经新建订单",7200);if (redisUtil.get("前缀_NEW_ORDER_" + json.getString("订单号")) == null){// 如果为上诉为空,说明订单状态变更在订单新增之前,我们就缓存一下300秒,至于多少秒看着办吧,300就太多了,但不影响redisUtil.set("前缀_STATUS_PUSH_" + json.getString("订单号"),JSON.toJSONString(order),300);// 因为在之前就不需要执行之后的代码了,返回就行return true;}// 删除redis多余的keyredisUtil.delete("前缀_ORDER_" + json.getString("订单号"));} catch (Exception e) {logger.error("订单状态推送同步锁失效",e);}finally {// 判断当前线程是否持有锁if (success && lock.isHeldByCurrentThread()) {lock.unlock();}}}// 如果新增在订单状态变更之前就可以直接入库了,如果订单状态变更在新建之前的话,上面有return操作,就不会执行入库操作statusChangeReceiptOperation(order);return true;} catch (Exception e) {logger.error("订单状态变更 报错:",e);return false;}}/*** 订单状态变更入库操作* @param order*/private void statusChangeReceiptOperation(Order order){try {// 订单状态变更入库操作。。。} catch (Exception e) {logger.error("订单状态变更报错:",e);}}/*** 新建订单*/public boolean newOrder(JSONObject json){try {// 入库操作,因为订单新增肯定是最开始的,所以根据json的参数直接入库就行了,我就不写了// 。。。// 加和上述相同的锁,因为订单号是一样的RLock lock = redisson.getLock("前缀_STATUS_SYNCHRONIZATION_" + json.getString("订单号"));// 60秒自旋拿锁,拿到锁之后持有60秒 (如果后面的值 <0 的话会使用开门狗机制,一直持有锁,除非项目挂掉了)boolean success = lock.tryLock(60L, 60L, TimeUnit.SECONDS);try {// 设置两个小时缓存,超时就认为不付款了redisUtil.set("前缀_NEW_ORDER_" + json.getString("订单号"),"等待付款",7200);// 去拿订单状态变更设置的redis的k-v键值对Object o = redisUtil.get("前缀_STATUS_PUSH_" + json.getString("订单号"));// 判断这里为不为空,因为加了锁,为空说明新增在订单状态变更之前,不需要多余操作if (o != null){// 如果不为空,说明状况变更在新增之前,取值然后入库JSONObject out2 = JSON.parseObject(o.toString());// json转为实体类Order order = JSONObject.toJavaObject(out2,Order.class);// 入库statusChangeReceiptOperation(order);// 删除redis多余的keyredisUtil.delete("前缀_STATUS_PUSH_" + json.getString("订单号"));}} catch (Exception e) {logger.error("订单新建推送同步锁失效",e);}finally {// 判断当前线程是否持有锁if (success && lock.isHeldByCurrentThread()) {lock.unlock();}}return true;} catch (Exception e) {logger.error("新建导购商户订单:",e);return false;}}
我遇见的问题
Factory method 'redisson' threw exception; nested exception is java.lang.NoSuchMethodError: io.netty.util.NetUtil.isIpV4StackPreferred()Z
猜测原因:NetUtil没有redisson需要的isIpV4StackPreferred()方法
排查:发现两个相同的class文件,那么肯定是jar包冲突了

查看依赖:

发现是这个包有问题:

去pom文件中排除:

总结
这只是我的思路和解决方法,如果有大佬有更好的办法,希望可以劳烦跟我探讨一下,共同成长,万分感谢
相关文章:
Redisson分布式锁实战
实战来源 此问题基于电商 这周遇见这么一个问题,简略的说一下 由MQ发布了两个消息,一个是订单新增,一个是订单状态变更 由于直接付款之后,这两个消息的发布时间不分先后,可能会造成两种情况,1、订单状态变更…...
JavaScript中循环遍历数组、跳出循环和继续循环
循环遍历数组 上个文章我们简单的介绍for循环,接下来,我们使用for循环去读取数据的数据,之前我们写过这样的一个数组,如下: const ITshareArray ["张三","二愣子","2033-1997","…...
Java——》Synchronized和Lock区别
推荐链接: 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…...
JDK20 + SpringBoot 3.1.0 + JdbcTemplate 使用
JDK20 SpringBoot 3.1.0 JdbcTemplate 使用 一.测试数据库 Postgres二.SpringBoot项目1.Pom 依赖2.配置文件3.启动类4.数据源配置类5.实体对象类包装类6.测试用实体对象1.基类2.扩展类 7.测试类 通过 JdbcTemplate 直接执行 SQL 语句,结合源码动态编译即可方便实现…...
CTFhub_SSRF靶场教程
CTFhub SSRF 题目 1. Bypass 1.1 URL Bypass 请求的URL中必须包含http://notfound.ctfhub.com,来尝试利用URL的一些特殊地方绕过这个限制吧 1.利用?绕过限制urlhttps://www.baidu.com?www.xxxx.me 2.利用绕过限制urlhttps://www.baidu.comwww.xxxx.me 3.利用斜…...
【华为OD机试】单词接龙【2023 B卷|100分】
【华为OD机试】-真题 !!点这里!! 【华为OD机试】真题考点分类 !!点这里 !! 题目描述: 单词接龙的规则是:可用于接龙的单词首字母必须要前一个单词的尾字母相同; 当存在多个首字母相同的单词时,取长度最长的单词,如果长度也相等, 则取字典序最小的单词;已经参与接龙…...
如何优雅的实现无侵入性参数校验之spring-boot-starter-validation
在开发过程中,参数校验是一个非常重要的环节。但是,传统的参数校验方法往往需要在代码中手动添加大量的 if-else 语句,这不仅繁琐,而且容易出错。为了解决这个问题,我们可以使用无侵入性参数校验的方式来简化代码并提高…...
企业架构LNMP学习笔记27
Keepalived的配置补充: 脑裂(裂脑):vip出现在了多台机器上。网络不通畅,禁用了数据包,主备服务器没法通讯,造成备服务器认为主服务器不可用,绑定VIP,主服务器VIP不会释放…...
品牌策划经理工作内容|工作职责|品牌策划经理做什么?
一位美国作家曾说过“品牌是一系列期望、记忆、故事和关系,他们共同构成了消费者最终原则一个产品或者服务的原因。” 所以,品牌经理这个岗位主要是创造感知价值主张,激发消费者购买这个品牌后带来的感知价值,这种回报的本质相对…...
【设计模式】三、概述分类+单例模式
文章目录 概述设计模式类型 单例模式饿汉式(静态常量)饿汉式(静态代码块)懒汉式(线程不安全)懒汉式(线程安全,同步方法)懒汉式(线程安全,同步代码块)双重检查静态内部类枚举单例模式在 JDK 应用的源码分析 …...
手把手教学 Springboot+ftp+下载图片
简单教学,复制即用的Ftp下载图片 引入配置包 <dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version></dependency><dependency><grou…...
LaaS LLM as a service
LaaS LLM as a service 核心构成GPT 产业链如何进行商业化LLM(Large Language Model) 发展和趋势LLM(Large Language Model) 对于行业公司的分层LLM(Large Language Model) 的机遇和挑战 LaaS LLM as a service 核心构成 计算:算力模型:算法输入&…...
数据结构与算法(一)数组的相关概念和底层java实现
一、前言 从今天开始,笔者也开始从0学习数据结构和算法,但是因为这次学习比较捉急,所以记录的内容并不会过于详细,会从基础和底层代码实现以及力扣相关题目去写相关的文章,对于详细的概念并不会过多讲解 二、数组基础…...
歌曲推荐《最佳损友》
最佳损友 陈奕迅演唱歌曲 《最佳损友》是陈奕迅演唱的一首粤语歌曲,由黄伟文作词,Eric Kwok(郭伟亮)作曲。收录于专辑《Life Continues》中,发行于2006年6月15日。 2006年12月26日,该曲获得2006香港新城…...
多元共进|科技促进艺术发展,助力文化传承
科技发展助力文化和艺术的传播 融合传统与创新,碰撞独特魅力 一起来了解 2023 Google 开发者大会上 谷歌如何依托科技创新 推动艺术与文化连接 传承和弘扬传统文化 自 2011 年成立以来,谷歌艺术与文化致力于提供体验艺术和文化的新方式,从生成…...
Java集合(Collection、Iterator、Map、Collections)概述——Java第十三讲
前言 本讲我们将继续来讲解Java的其他重要知识点——Java集合。Java集合框架是Java编程语言中一个重要的部分,它提供了一套预定义的类和接口,供程序员使用数据结构来存储和操作一组对象。Java集合框架主要包括两种类型:一种是集合(Collection),存储一个元素列表,…...
topscoding主题库模板题
目录 模板题 【模板题】分因数(P1101) 【模板题】区间素数 III(P1113) 进制转换 III (任意转任意) (P2463) AB Problem(高精度加法) A-B Problem(高精度减法&…...
Linux--进程间通讯--FIFO(open打开)
1. 什么是FIFO FIFO命名管道,也叫有名管道,来区分管道pipe。管道pipe只能用于有血缘关系的进程间通信,但通过FIFO可以实现不相关的进程之间交换数据。FIFO是Linux基础文件类型中的一种,但是FIFO文件在磁盘上没有数据块,…...
哪里可以了解轻量的工作流引擎?
如果想要实现高效率的办公,可以使用轻量的工作流引擎低代码技术平台。随着工作量日益繁重起来,传统的办公制作方式已经无法满足现实需要的,采用轻量级的表格制作工具,就能在无形中缓解办公压力,创造更高效、灵活、优质…...
lvs负载均衡、LVS集群部署
四:LVS集群部署 lvs给nginx做负载均衡项目 218lvs(DR 负载均衡器) yum -y install ipvsadm(安装这个工具来管理lvs) 设置VIP192.168.142.120 创建ipvsadm的文件用来存放lvs的规则 定义策略 ipvsadm -C //清空现有…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
