Spring Boot 集成Redisson实现分布式锁详细案例
作者:剑圣无痕??????? 发布时间:2023-06-10 05:09:17
标签:Spring,Boot,集成,Redisson,分布式,锁
前言
Spring Boot集成Redis实现单机分布式锁针对单机分布式锁还是存在锁定续期、可重入的问题,本文将采用Spring Boot 集成Ression实现分布式锁进行详细讲解。
分布式锁实现
引入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.6</version>
</dependency>
说明:关于集成Redisson,我们需要注意与Spring Boot的版本对应。
具体对应的关系如下:
注意:3.13.6对应的Spring Boot的版本为2.3.0,而redis-spring-data为redis-spring-data-23。我们可以通过查看pom文件的引用从而得到依赖关系。
Redisson的配置
application.yml中引入redisson.yml配置
redis:
redisson:
file: classpath:redisson.yml
redisson.yml配置
singleServerConfig:
password: xxxx
address: "redis://127.0.0.1:6379"
database: 1
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.FstCodec> {}
transportMode: "NIO"
说明:本文配置的是单机环境,如果需要配置集群环境,可以采用如下配置:
clusterServersConfig:
idleConnectionTimeout: 10000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
failedSlaveReconnectionInterval: 3000
failedSlaveCheckInterval: 60000
password: null
subscriptionsPerConnection: 5
clientName: null
loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
slaveConnectionMinimumIdleSize: 24
slaveConnectionPoolSize: 64
masterConnectionMinimumIdleSize: 24
masterConnectionPoolSize: 64
readMode: "SLAVE"
subscriptionMode: "SLAVE"
nodeAddresses:
- "redis://127.0.0.1:7004"
- "redis://127.0.0.1:7001"
- "redis://127.0.0.1:7000"
scanInterval: 1000
pingConnectionInterval: 0
keepAlive: false
tcpNoDelay: false
threads: 16
nettyThreads: 32
codec: !<org.redisson.codec.MarshallingCodec> {}
transportMode: "NIO"
封装Redisson工具类
@Component
public class RedissonLockUtil
{
private static final Logger logger = LoggerFactory.getLogger(RedissonLockUtil.class);
@Autowired
private RedissonClient redissonClient;
/**
* 加锁
* @param lockKey
* @return
*/
public RLock lock(String lockKey)
{
RLock lock = redissonClient.getLock(lockKey);
return lock;
}
/**
* 公平锁
* @param key
* @return
*/
public RLock fairLock(String key)
{
return redissonClient.getFairLock(key);
}
/**
* 带超时的锁
* @param lockKey
* @param timeout 超时时间 单位:秒
*/
public RLock lock(String lockKey, int timeout)
{
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, TimeUnit.SECONDS);
return lock;
}
/**
* 读写锁
* @param key
* @return
*/
public RReadWriteLock readWriteLock(String key) {
return redissonClient.getReadWriteLock(key);
}
/**
* 带超时的锁
* @param lockKey
* @param unit 时间单位
* @param timeout 超时时间
*/
public RLock lock(String lockKey, TimeUnit unit ,int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
/**
* 加锁
* @param key
* @param supplier
* @return
*/
public <T> T lock(String key, Supplier<T> supplier) {
RLock lock = lock(key);
try {
lock.lock();
return supplier.get();
} finally {
if (lock != null && lock.isLocked()) {
lock.unlock();
}
}
}
/**
* 尝试获取锁
* @param lockKey
* @param waitTime 等待时间
* @param leaseTime 自动释放锁时间
* @return
*/
public boolean tryLock(String lockKey, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return false;
}
}
/**
* 尝试获取锁
* @param lockKey
* @param unit 时间单位
* @param waitTime 等待时间
* @param leaseTime 自动释放锁时间
* @return
*/
public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
return false;
}
}
/**
* 释放锁
* @param lockKey
*/
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
/**
* 释放锁
* @param lock
*/
public void unlock(RLock lock)
{
lock.unlock();
}
}
模拟秒杀扣减库存
public int lockStock()
{
String lockKey="lock:stock";
String clientId = UUID.randomUUID().toString();
//加锁
RLock lock=redissonLockUtil.lock(lockKey);
lock.lock();
try
{
logger.info("加锁成功 clientId:{}",clientId);
int stockNum= Integer.valueOf((String)redisUtil.get("seckill:goods:stock"));
if(stockNum>0)
{
stockNum--;
redisUtil.set("seckill:goods:stock",String.valueOf(stockNum));
logger.info("秒杀成功,剩余库存:{}",stockNum);
}
else
{
logger.error("秒杀失败,剩余库存:{}", stockNum);
}
//获取库存数量
return stockNum;
}
catch (Exception e)
{
logger.error("decry stock eror",e);
}
finally
{
if(lock!=null)
{
lock.unlock();
}
}
return 0;
}
测试代码
@RequestMapping("/redisLockTest")
public void redisLockTest()
{
// 初始化秒杀库存数量
redisUtil.set("seckill:goods:stock", "10");
List<Future> futureList = new ArrayList<>();
//多线程异步执行
ExecutorService executors = Executors.newScheduledThreadPool(10);
//
for (int i = 0; i < 30; i++)
{
futureList.add(executors.submit(this::lockStock));
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
logger.error("redisLockTest error",e);
}
}
// 等待结果,防止主线程退出
futureList.forEach(t -> {
try
{
int stockNum =(int) t.get();
logger.info("库存剩余数量:{}",stockNum);
}
catch (Exception e)
{
logger.error("get stock num error",e);
}
});
}
执行结果如下:
来源:https://juejin.cn/post/7128050664336261157


猜你喜欢
- 业务背景电商订单项目分正向和逆向两个部分:其中正向数据库记录了订单的基本信息,包括订单基本信息、订单商品信息、优惠卷信息、发票信息、账期信息
- Volley简介我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发
- 背景年初开始我们就开始了关于Gradle Sync阶段的优化。之前和大家都简单的介绍过工程相关的背景情况了,我们大概有400+的Module
- c语言关闭socket的两种方式一、shutdown()#include<sys/socket.h>int shutdown(i
- 解决项目中表单重复提交的问题,在平常的项目中有以下几种可能出现表单重复提交的情况,比如说:1.由于服务器缓慢或者网络延迟的原因,重复点击提交
- 在使用 SpringMVC 上传文件时,接收到的文件格式为 MultipartFile,但是在很多场景下使用都需要File格式的文件,记录下
- 前言本文,将介绍如何通过Java后端程序代码在PDF中创建工具提示。添加工具提示后,当鼠标悬停在页面上的元素时,将显示工具提示内容。导入ja
- 对这种懒加载问题,最后的做法是利用Spring提供的一个针对Hibernate的一个支持类,其主要意思是在发起一个页面请求时打开Hibern
- 废话不多说了,直接给大家贴代码了。package com.only.android.app;import java.io.File;impo
- 本文以Java代码为例介绍如何实现将彩色PDF文件转为灰度(黑白)的PDF文件,即:将PDF文档里面的彩色图片或者文字等通过调用PdfGra
- 一、日志的分类1、名字分类log4j :log for java (因为for和4读音差不多,所以交log4j)logBack 日志说明注意
- 最近过年发红包拜年成为一种新的潮流,作为程序猿对算法的好奇远远要大于对红包的好奇,这里介绍一种自己想到的一种随机红包分配策略,还请大家多多指
- 在android开发中我们常常遇到与到乱码问题,遇到乱码问题首先我们要先检查两端编码格式是否一致!一般我们提交数据用get 和post方法,
- 环境:springcloud Hoxton.SR11本节主要了解系统中的谓词与配置的路由信息是如何进行初始化关联生成路由对象的。每个谓词工厂
- 本文实例介绍了Android如何画出触摸轨迹的方法,分享给大家供大家参考,具体内容如下效果图:实现代码:package com.androi
- 最近有一款2048的游戏非常火,本文将来介绍一下使用OGEngine游戏引擎开发游戏2048。OGEngine引擎是开源的,我们很容易找到,
- springboot启动时自动加载application.properties或者application.yml,如何定义自己的配置让spr
- Stream简化元素计算一、接口设计从Java1.8开始提出了Stream流的概念,侧重对于源数据计算能力的封装,并且支持序列与并行两种操作
- C#小程序飞行棋,程序效果图1、设计分析这个程序界面大致分为四部分:① 最上面游戏名字界面②信息提示区③游戏界面区④游戏操作提示区2、分区设
- 一、 ASCII码我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位(bit)有0和1两种状态,因此八个二进制