一、使用

1、pom.xml导入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、配置文件

@Configuration
public class RedissonConfig { // 自定义在yml或properties文件中
@Value("${spring.redis.host}")
private String host; @Value("${spring.redis.port}")
private String port; @Value("${spring.redis.password}")
private String password; @Bean
public RedissonClient getRedisson() {
Config config = new Config();
if (StringUtils.isBlank(password)) {
config.useSingleServer().setAddress("redis://" + host + ":" + port);
} else {
config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
}
return Redisson.create(config);
} }

3、使用类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.redis.util.RedisLockRegistry; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock; /**
* @author Forlan
* @since 2022-04-03
*/
public class ForlanTest { private static final Logger logger = LoggerFactory.getLogger(ForlanTest.class); @Autowired
private RedisLockRegistry redisLockRegistry; public void test() {
String lockKey = "key_id";
Lock loginLock = redisLockRegistry.obtain(lockKey.intern());
boolean getLock = false;
try {
// tryLock()底层调用this.tryLock(0L, TimeUnit.MILLISECONDS)
getLock = loginLock.tryLock(5, TimeUnit.SECONDS);
if (getLock) {
//获得锁执行业务
}
} catch (Exception e) {
logger.error("异常信息...", e);
} finally {
if (getLock) {
//释放锁
loginLock.unlock();
}
}
}
}

二、原理

obtain方法

private final Map<String, RedisLockRegistry.RedisLock> locks;

private final class RedisLock implements Lock {
private final String lockKey;
private final ReentrantLock localLock;
private volatile long lockedAt; private RedisLock(String path) {
this.localLock = new ReentrantLock();
this.lockKey = this.constructLockKey(path);
}
} public Lock obtain(Object lockKey) {
Assert.isInstanceOf(String.class, lockKey);
String path = (String)lockKey;
return (Lock)this.locks.computeIfAbsent(path, (x$0) -> {
return new RedisLockRegistry.RedisLock(x$0);
});
}

主要是根据lockKey去查locks这个map中是否已经存在这个key

如果存在就返回内部类RedisLock

如果不存在就创建一个RedisLock,以lockKey为key,RedisLock为value放入map中

备注:每个分布式应用自己都会创建一个RedisLockRegistry实例,同一个应用的多个线程共享RedisLock类

tryLock方法

public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
long now = System.currentTimeMillis();
// 尝试拿取本地锁
if (!this.localLock.tryLock(time, unit)) {
return false;
} else {
try {
long expire = now + TimeUnit.MILLISECONDS.convert(time, unit); boolean acquired;
// 当前时间还没过期并且还未获得redis锁,睡眠100ms继续重试
while(!(acquired = this.obtainLock()) && System.currentTimeMillis() < expire) {
Thread.sleep(100L);
} if (!acquired) {
this.localLock.unlock();
} return acquired;
} catch (Exception var9) {
this.localLock.unlock();
this.rethrowAsLockException(var9);
return false;
}
}
}

主要过程

先获得本地锁,拿不到直接返回失败

当前时间还没过期并且还没拿到redis锁,睡眠100ms继续重试

如果拿到redis锁,结束循环,返回成功

如果超时了还没拿到,释放锁,返回失败

拿redis锁的过程

private boolean obtainLock() {
boolean success = (Boolean)RedisLockRegistry.this.redisTemplate.execute(RedisLockRegistry.this.obtainLockScript, Collections.singletonList(this.lockKey), new Object[]{RedisLockRegistry.this.clientId, String.valueOf(RedisLockRegistry.this.expireAfter)});
if (success) {
this.lockedAt = System.currentTimeMillis();
} return success;
}

通过obtainLock方法,执行lua脚本来获取

redisTemplate.execute()参数说明:

第一个obtainLockScript参数就是要执行的lua脚本;

local lockClientId = redis.call('GET', KEYS[1])
if lockClientId == ARGV[1] then
redis.call('PEXPIRE', KEYS[1], ARGV[2])
return true
elseif not lockClientId then
redis.call('SET', KEYS[1], ARGV[1], 'PX', ARGV[2])
return true
end
return false

第二个参数就是表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用1为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推);

第三个参是附加参数 arg [arg …] ,可以在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)

为什么要用本地锁

  • 为了可重入
  • 为了减轻redis服务器的压力

为什么要用lua脚本

  • 保证原子性
  • 减少网络开销
  • 替代redis的事务功能

unlock方法

public void unlock() {
if (!this.localLock.isHeldByCurrentThread()) {
throw new IllegalStateException("You do not own lock at " + this.lockKey);
} else if (this.localLock.getHoldCount() > 1) {
this.localLock.unlock();
} else {
try {
if (Thread.currentThread().isInterrupted()) {
RedisLockRegistry.this.executor.execute(this::removeLockKey);
} else {
this.removeLockKey();
}
if (RedisLockRegistry.logger.isDebugEnabled()) {
RedisLockRegistry.logger.debug("Released lock; " + this);
}
} catch (Exception var5) {
ReflectionUtils.rethrowRuntimeException(var5);
} finally {
this.localLock.unlock();
}
}
}

释放锁的过程

1、判断是否是当前线程持有锁,如果不是,抛异常(本地锁)

2、判断当前线程持有锁的计数

如果当前线程持有锁的计数 > 1,说明本地锁被当前线程多次获取,这时只会释放本地锁,释放之后当前线程持有锁的计数-1。

否则,释放本地锁和redis锁。

Redis分布式实现原理的更多相关文章

  1. Redis分布式锁原理

    1. Redis分布式锁原理 1.1. Redisson 现在最流行的redis分布式锁就是Redisson了,来看看它的底层原理就了解redis是如何使用分布式锁的了 1.2. 原理分析 分布式锁要 ...

  2. redis分布式锁原理与实现

    分布式锁原理 分布式锁,是控制分布式系统之间同步访问共享资源的一种方式.在分布式系统中,常常需要协调他们的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候, ...

  3. 分布式-技术专区-Redis分布式锁原理实现

    在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务.分布式锁等.那具体什么是分布式锁,分布式锁应用在哪些业务场景.如何来实现分布式锁呢?今天来探讨分布式锁这个话题. ...

  4. Java进阶专题(二十五) 分布式锁原理与实现

    前言 ​ 现如今很多系统都会基于分布式或微服务思想完成对系统的架构设计.那么在这一个系统中,就会存在若干个微服务,而且服务间也会产生相互通信调用.那么既然产生了服务调用,就必然会存在服务调用延迟或失败 ...

  5. 利用多写Redis实现分布式锁原理与实现分析(转)

    利用多写Redis实现分布式锁原理与实现分析   一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子:场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能 ...

  6. 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁

    首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...

  7. Redisson实现Redis分布式锁的底层原理

    一.写在前面 现在面试,一般都会聊聊分布式系统这块的东西.通常面试官都会从服务框架(Spring Cloud.Dubbo)聊起,一路聊到分布式事务.分布式锁.ZooKeeper等知识.所以咱们这篇文章 ...

  8. 项目中用到了Redis分布式锁,了解一下背后的原理

    前言 以前在学校做小项目的时候,用到Redis,基本也只是用来当作缓存.现在博主在某金融平台实习,发现Redis在生产中并不只是当作缓存这么简单.在我接触到的项目中,Redis起到了一个分布式锁的作用 ...

  9. Redis分布式锁的正确使用与实现原理

    模拟一个电商里面下单减库存的场景. 1.首先在redis里加入商品库存数量. 2.新建一个Spring Boot项目,在pom里面引入相关的依赖. <dependency> <gro ...

随机推荐

  1. 聊聊视频中的编解码器,你所不知道的h264、h265、vp8、vp9和av1编解码库

    你知道FFmpeg吗?了解过h264/h265/vp8/vp9编解码库吗? 我们日常生活中使用最广泛的五种视频编码:H264(AVC).H265(HEVC).vp8.vp9.av1都分别是什么?由哪些 ...

  2. CF1117G题解

    题意:区间建笛卡尔树,求每个节点的siz之和. 首先看到笛卡尔树,就应该想到,因为这是一个排列,可以找到通过左边和右边第一个比自己大的元素来"建立"笛卡尔树. 设 \(l(u)\) ...

  3. ## [湖南省赛2019]Findme ###

    [湖南省赛2019]Findme 1.题目概述 2.解题过程 010打开这几张图片 先简单分析一下这几张图片 简单分析 1.png 从外观上,1.png明显高度太低,需要更改 2.png 2.png末 ...

  4. Matplotlib库基础_一

    Matplotlib库基础 •pyplot绘制坐标 plt.plot(x,y,format_string,**kwargs) x:x轴数据,列表或数组,可选 y:y轴数据,列表或数组 format_s ...

  5. Oracle视图(view)传参数教程

    废话不多说,直接上例子! 创建包: create or replace package p_view_param is function set_param(num number) return nu ...

  6. dedecms 5.7 任意前台用户修改漏洞

    一. 启动环境 1.双击运行桌面phpstudy.exe软件 2.点击启动按钮,启动服务器环境 二.代码审计 1.双击启动桌面Seay源代码审计系统软件 2.点击新建项目按钮,弹出对画框中选择(C:\ ...

  7. Windows 7/8 64位系统 不能注册32位dll 文件的解决方案

    这几天碰到一个问题,运行一个易语言开发的软件出现以下错误.我的系统是 Windows7 64 位 专业版.在系统盘 windows/system32  下查找 dm.dll.但是没有这个文件.于是我到 ...

  8. POI完成Excel文件的读和写

    简介 Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office(Excel.WORD.PowerPo ...

  9. PWA全称Progressive Web App,即渐进式WEB应用?

    一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用. 随后添加上 App Manifest 和 Service Worker 来实现 PWA 的安装和离线等功能解决了哪些问题 ...

  10. 【Java面试宝典】什么情况下会发生栈内存溢出?

    如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常. 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常.