《微服务实战》 第二十八章 分布式锁框架-Redisson
前言
Redisson 在基于 NIO 的 Netty 框架上,充分的利⽤了 Redis 键值数据库提供的⼀系列优势,在Java 实⽤⼯具包中常⽤接⼝的基础上,为使⽤者提供了⼀系列具有分布式特性的常⽤⼯具类。使得原本作为协调单机多线程并发程序的⼯具包获得了协调分布式多机多线程并发系统的能⼒,⼤⼤降低了设计和研发⼤规模分布式系统的难度。同时结合各富特⾊的分布式服务,更进⼀步简化了分布式环境中程序相互之间的协作。
1、redisson工作原理
2、看门狗原理
A服务先运行,在运行B服务,还没释放A的锁,A就挂了,会不会死锁呢?
答:没有导致死锁,因为底层有看门狗机制
默认指定锁时间为30s(看门狗时间)
锁的自动续期:若是业务超长,运行期间自动给锁上新的 30s,不用担心业务时间过长,锁就自动过期
加锁的业务只要运行完成,就不会给当前锁续期,及时不手动解锁,锁默认在30s 后自动删除。
3、spring boot与redisson的整合
3.1、添加库存服务:
stock-service
3.2、添加依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>2021.0.4.0</version></dependency>--><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.19.1</version></dependency>
3.3、添加配置
3.3.1、单机
redisson:addr:singleAddr:host: redis://localhost:6379password: 123456database: 0pool-size: 10
3.3.2、集群
redisson:addr:cluster:hosts: redis://47.96.11.185: 6370,...,redis://47.96.11.185:6373password : 123456
3.3.3、主从
redisson:addr:masterAndSlave:masterhost: redis : //47.96.11.185 : 6370slavehosts: redis://47.96.11.185: 6371,redis://47.96.11.185:6372password : 123456database : 0
3.4、配置RedissonClient
3.4.1、单机
/*** 配置RedissonClient*/
@Configuration
public class RedissonConfig {@Value("${redisson.addr.singleAddr.host}")private String host;@Value("${redisson.addr.singleAddr.password}")private String password;@Value("${redisson.addr.singleAddr.database}")private int database;@Value("${redisson.addr.singleAddr.pool-size}")private int poolSize;@Beanpublic RedissonClient redissonClient(){Config config = new Config();config.useSingleServer().setAddress(host).setPassword(password).setDatabase(database).setConnectionPoolSize(poolSize).setConnectionMinimumIdleSize(poolSize);return Redisson.create(config);}
}
3.4.2、集群
/*** 配置RedissonClient*/
@Configuration
public class RedissonConfig {@Value("${redisson.addr.cluster.hosts}")private String hosts;@Value("${redisson.addr.cluster.password}")private String password;/*** 集群模式** @return*/@Beanpublic RedissonClient redissonClient() {Config config = new Config();config.useClusterServers().addNodeAddress(hosts.split("[,]")).setPassword(password).setScanInterval(2000).setMasterConnectionPoolSize(10000).setSlaveConnectionPoolSize(10000);return Redisson.create(config);}
}
3.4.3、集群
/*** 配置RedissonClient*/
@Configuration
public class RedissonConfig {@Value("${redisson.addr.masterAndSlave.masterhost}")private String masterhost;@Value("${redisson.addr.masterAndSlave.slavehosts}")private String slavehosts;@Value("${redisson.addr.masterAndSlave.password}")private String password;@Value("${redisson.addr.masterAndSlave.database}")private int database;/*** 主从模式** @return*/@Beanpublic RedissonClient redissonClient() {Config config = new Config();config.useMasterSlaveServers().setMasterAddress(masterhost).addSlaveAddress(slavehosts.split("[,]")).setPassword(password).setDatabase(database).setMasterConnectionPoolSize(10000).setSlaveConnectionPoolSize(10000);return Redisson.create(config);}
}
3.5、Redisson的使用
- 获取锁 —— 公平锁和⾮公平锁
// 获取公平锁
RLock lock = redissonClient . getFairLock ( skuId );
// 获取⾮公平锁
RLock lock = redissonClient . getLock ( skuId ); - 加锁 —— 阻塞锁和⾮阻塞锁
// 阻塞锁(如果加锁成功之后,超时时间为 30s ;加锁成功开启看⻔狗,剩 5s 延⻓过期时间)
lock . lock ();
// 阻塞锁(如果加锁成功之后,设置⾃定义 20s 的超时时间)
lock . lock ( 20 , TimeUnit . SECONDS );
// ⾮阻塞锁(设置等待时间为 3s ;如果加锁成功默认超时间为 30s )
boolean b = lock . tryLock ( 3 , TimeUnit . SECONDS );
// ⾮阻塞锁(设置等待时间为 3s ;如果加锁成功设置⾃定义超时间为 20s )
boolean b = lock . tryLock ( 3 , 20 , TimeUnit . SECONDS ); - 释放锁
lock . unlock (); - 应⽤示例
// 公平⾮阻塞锁
RLock lock = redissonClient . getFairLock ( skuId );
boolean b = lock . tryLock ( 3 , 20 , TimeUnit . SECONDS ); - 减库存加锁案例
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeUnit;@RestController
@RequestMapping("/stock")
public class StockController {@Autowiredprivate RedissonClient redissonClient;@GetMapping("/reduceStock")public void reduceStock(@RequestParam String productId){// 获取⾮公平锁RLock lock = this.redissonClient.getLock("stock:" + productId);// 阻塞锁(如果加锁成功之后,设置⾃定义 20s 的超时时间)lock.lock(30, TimeUnit.SECONDS);System.out.println("加锁成功." + Thread.currentThread().getName());try {TimeUnit.SECONDS.sleep(25);} catch (InterruptedException e) {e.printStackTrace();}finally {System.out.println("解锁成功." + Thread.currentThread().getName());lock.unlock();}}
}
测试:浏览器发起两次两次减库存
http://localhost:8099/stock/reduceStock?productId=001
3.6、aop实现分布式锁
3.6.1、定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeLock {/*** 参数下标* @return*/int[] lockIndex() default {-1} ;/*** 锁的等待时间* @return*/long waitTime() default 3000;/*** 时间单位* @return*/TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}
3.6.2、定义切面
/*** 定义分布式锁的切面*/
@Component
@Aspect
public class DistributeLockAspect {@Autowiredprivate RedissonClient redissonClient;@Around(value = "@annotation(lock)")public void distibuteLock(ProceedingJoinPoint proceedingJoinPoint, DistributeLock lock){Signature signature = proceedingJoinPoint.getSignature();StringBuilder stringBuilder = new StringBuilder();//方法所属的类String declaringTypeName = signature.getDeclaringTypeName();String name = signature.getName();stringBuilder.append(declaringTypeName);stringBuilder.append(name);//获取调用方法的参数Object[] args = proceedingJoinPoint.getArgs();int[] ints = lock.lockIndex();if(args != null) {final int length = args.length;if (length >0) {//考虑下标越界for (int anInt : ints) {//把合法下标值放到sbif (anInt >= 0 && anInt < length){stringBuilder.append(JSON.toJSONString(args[anInt]));}}}}//将方法的信息转成md5,作为锁的标识String key = SecureUtil.md5(stringBuilder.toString());//获取锁RLock rLock = redissonClient.getLock(key);//从注解获取时间单位TimeUnit timeUnit = lock.timeUnit();//从注解等待时间long waitTime = lock.waitTime();//执行业务代码try {//加锁rLock.tryLock(waitTime,timeUnit);System.out.println("成功加锁。" + Thread.currentThread().getName());proceedingJoinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}finally {//解锁rLock.unlock();System.out.println("成功解锁。" + Thread.currentThread().getName());}}
}
注解的使用:
@DistributeLock(lockIndex = {0,1},waitTime = 3,timeUnit = TimeUnit.SECONDS)
相关文章:

《微服务实战》 第二十八章 分布式锁框架-Redisson
前言 Redisson 在基于 NIO 的 Netty 框架上,充分的利⽤了 Redis 键值数据库提供的⼀系列优势,在Java 实⽤⼯具包中常⽤接⼝的基础上,为使⽤者提供了⼀系列具有分布式特性的常⽤⼯具类。使得原本作为协调单机多线程并发程序的⼯具包获得了协调…...
局部搜索,变邻域搜索算法
目录 局部搜索 02 变邻域搜索算法 局部搜索 1.1 局部搜索是什么玩意儿? 官方一点:局部搜索是解决优化问题的一种启发式算法。对于某些计算起来非常复杂的优化问题,比如各种NP-难问题,要找到最优解需要的时间随问题规模呈指数增长,因此诞生了各种启发式算法来退而求其次…...
软件工程实训——第一天
第一天 前后分离 前端:android 后端:springbootmbatis-plus 高心星 软件工程的思维来开发项目 问题定义 可行性研究 需求分析 概要设计 详细设计 编码 测试 维护 需求分析 1.用户的信息管理 2.新增支出 3.新增收入 4.支出统计 5.收入…...

嵌入式C语言中if/else如何优化详解
观点一(灵剑): 前期迭代懒得优化,来一个需求,加一个if,久而久之,就串成了一座金字塔。 当代码已经复杂到难以维护的程度之后,只能狠下心重构优化。那,有什么方案可以优雅…...

【LSTM】读取时间序列数据 | 时间序列数据的小批量划分方法
由于序列数据本质上是连续的,因此我们在处理数据时需要解决这个问题。当序列过长而不能被模型一次性全部处理时,我们希望能拆分这样的序列以便模型方便读取。 Q:怎样随机生成一个具有n个时间步的mini batch的特征和标签? A&…...

K8s in Action 阅读笔记——【12】Securing the Kubernetes API server
K8s in Action 阅读笔记——【12】Securing the Kubernetes API server 12.1 Understanding authentication 在上一章中,我们提到API服务器可以配置一个或多个认证插件(授权插件也是同样的情况)。当API服务器接收到一个请求时,它…...

爆肝整理,3个月从功能进阶自动化测试,一跃成测试卷王...
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 首先先了解自动化…...
人生这场概率游戏,怎么玩
只会标准答案,是不可救药的愚蠢 那么为了便于理解,我用一些典型的案例来讲解,什么是概率游戏,以及这个游戏,应该怎么玩。 比如典型的相亲,婚恋。人生大事,用标准答案来说,你的意中人…...

Redis笔记
缓存过期时间很重要!redis是单线程的 对于内存过多的3中方案: 惰性删除: 在定时删除的基础上,对于已经过期了的数据,redis的随机选择算法一直没有选中这个数据,所以导致它就一直没被删除,但是…...
centos 安装supervisor并运行网站
前言 之前一直用宝塔的**进程守护管理器【Supervisor】**来启动一些项目,如ThinkPHP、Hyperf的项目,或laravel的一些命令。如果不用宝塔怎么办呢? 一、简介[supervisor] [Supervisor] 是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支…...
Hadoop面试题十道
问题 1:Hadoop是什么? 答案:Hadoop是一个开源的分布式计算框架,用于处理大规模数据集的存储和处理。它基于Google的MapReduce和Google文件系统(GFS)的思想,旨在解决大数据量的处理和分析问题。…...

使用Docker-Compose对Docker容器集群快速编排
目录 一、Docker-Compose1、Docker-Compose使用场景2、Docker-Compose简介3、Docker-Compose安装部署4、YAML 文件格式及编写注意事项5、Docker Compose配置常用字段6、Docker Compose 常用命令7、Docker Compose 文件结构8、docker Compose撰写nginx 镜像9、docker Compose撰写…...

React-Redux 对Todolist修改
在单独使用redux的时候 需要手动订阅store里面 感觉特别麻烦 不错的是react有一个组件可以帮我们解决这个问题, 那就是react-redux。 react-redux提供了Provider 和 connent给我们使用。 先说一下几个重点知道的知识 Provider 就是用来提供store里面的状态 自动getState()co…...
初识微信小程序
新建小程序 创建一个新的微信小程序项目: 打开微信开发者工具,点击“新建项目”。 在弹出的窗口中,填写小程序的 AppID、项目名称和项目目录等信息。 点击“确定”按钮,等待微信开发者工具自动下载并安装所需的依赖库和框架。 …...
我们该如何入门编程呢
提醒:以下内容仅做参考,可自行发散。在发布作品前,请把不需要的内容删掉。 随着信息技术的快速发展,编程已经成为一个越来越重要的技能。那么,我们该如何入门编程呢?选择编程语言:选择一种编程…...
App 软件开发《判断6》试卷及答案
App 软件开发《判断6》试卷及答案 文章目录 App 软件开发《判断6》试卷及答案判断题(对的打“√”,错的打“”;共0分)1.”ionic resources --icon"命令用于生成适应不同分辨率的App图标所应用的图片。(✔)2&#…...
MVC工作原理
MVC工作原理 有视图的情况 1.客户端(浏览器)发起请求,DispatcherServlet拦截请求。 2.DispatcherServlet根据请求信息调用HandlerMapping。HandlerMapping根据uri去匹配查询能处理的Handler(也就是我们所说的Controller&#x…...
使用 Redis 统计网站 UV 的方法
使用 Redis 统计网站 UV 的方法(概率算法) 文章目录 前言思路HyperLogLog 使用 Redis 命令操作使用 Java 代码操作 HyperLogLog 实现原理及特点使用 Java 实现 HyperLogLog小结 前言 网站 UV 就是指网站的独立用户访问量Unique Visitor,即相同用户的多次访问需要…...

黑客工具软件大全
黑客工具软件大全100套 给大家准备了全套网络安全梓料,有web安全,还有渗透测试等等内容,还包含电子书、面试题、pdf文档、视频以及相关的网络安全笔记 👇👇👇 《黑客&网络安全入门&进阶学习包》 &a…...

uniapp主题切换功能的第二种实现方式(scss变量+require)
在上一篇 “uniapp主题切换功能的第一种实现方式(scss变量vuex)” 中介绍了第一种如何切换主题,但我们总结出一些不好的地方,例如扩展性不强,维护起来也困难等等,那么接下我再给大家介绍另外一种切换主题的…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
文件上传漏洞防御全攻略
要全面防范文件上传漏洞,需构建多层防御体系,结合技术验证、存储隔离与权限控制: 🔒 一、基础防护层 前端校验(仅辅助) 通过JavaScript限制文件后缀名(白名单)和大小,提…...

【若依】框架项目部署笔记
参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作: 压缩包下载:http://download.redis.io/releases 1. 上传压缩包,并进入压缩包所在目录,解压到目标…...