300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Redisson实现Redis分布式锁的N种姿势

Redisson实现Redis分布式锁的N种姿势

时间:2021-04-19 02:41:05

相关推荐

Redisson实现Redis分布式锁的N种姿势

点击蓝色“程序猿DD”关注我哟

来源:阿飞的博客

前几天发的一篇文章《Redlock:Redis分布式锁最牛逼的实现》,引起了一些同学的讨论,也有一些同学提出了一些疑问,这是好事儿。本文在讲解如何使用Redisson实现Redis普通分布式锁,以及Redlock算法分布式锁的几种方式的同时,也附带解答这些同学的一些疑问。

Redis几种架构

Redis发展到现在,几种常见的部署架构有:

单机模式;

主从模式;

哨兵模式;

集群模式;

我们首先基于这些架构讲解Redisson普通分布式锁实现,需要注意的是,只有充分了解普通分布式锁是如何实现的,才能更好的了解Redlock分布式锁的实现,因为Redlock分布式锁的实现完全基于普通分布式锁

普通分布式锁

Redis普通分布式锁原理这个大家基本上都了解,本文不打算再过多的介绍,上一篇文章Redlock:Redis分布式锁的实现也讲的很细,并且也说到了几个重要的注意点。如果你对Redis普通的分布式锁还有一些疑问,可以再回顾一下这篇文章。

接下来直接show you the code,毕竟 talk is cheap。

redisson版本

本次测试选择redisson 2.14.1版本。

单机模式

源码如下:

//构造redisson实现分布式锁必要的Config

Configconfig=newConfig();

config.useSingleServer().setAddress("redis://172.29.1.180:5379").setPassword("a123456").setDatabase(0);

//构造RedissonClient

RedissonClientredissonClient=Redisson.create(config);

//设置锁定资源名称

RLockdisLock=redissonClient.getLock("DISLOCK");

booleanisLock;

try{

//尝试获取分布式锁

isLock=disLock.tryLock(500,15000,TimeUnit.MILLISECONDS);

if(isLock){

//TODOifgetlocksuccess,dosomething;

Thread.sleep(15000);

}

}catch(Exceptione){

}finally{

//无论如何,最后都要解锁

disLock.unlock();

}

通过代码可知,经过Redisson的封装,实现Redis分布式锁非常方便,我们再看一下Redis中的value是啥,和前文分析一样,hash结构,key就是资源名称,field就是UUID+threadId,value就是重入值,在分布式锁时,这个值为1(Redisson还可以实现重入锁,那么这个值就取决于重入次数了):

172.29.1.180:5379>hgetallDISLOCK

1)"01a6d806-d282-4715-9bec-f51b9aa98110:1"

2)"1"

哨兵模式

即sentinel模式,实现代码和单机模式几乎一样,唯一的不同就是Config的构造:

Configconfig=newConfig();

config.useSentinelServers().addSentinelAddress(

"redis://172.29.3.245:26378","redis://172.29.3.245:26379","redis://172.29.3.245:26380")

.setMasterName("mymaster")

.setPassword("a123456").setDatabase(0);

集群模式

集群模式构造Config如下:

Configconfig=newConfig();

config.useClusterServers().addNodeAddress(

"redis://172.29.3.245:6375","redis://172.29.3.245:6376","redis://172.29.3.245:6377",

"redis://172.29.3.245:6378","redis://172.29.3.245:6379","redis://172.29.3.245:6380")

.setPassword("a123456").setScanInterval(5000);

总结

普通分布式实现非常简单,无论是那种架构,向Redis通过EVAL命令执行LUA脚本即可。

Redlock分布式锁

那么Redlock分布式锁如何实现呢?以单机模式Redis架构为例,直接看实现代码:

Configconfig1=newConfig();

config1.useSingleServer().setAddress("redis://172.29.1.180:5378")

.setPassword("a123456").setDatabase(0);

RedissonClientredissonClient1=Redisson.create(config1);

Configconfig2=newConfig();

config2.useSingleServer().setAddress("redis://172.29.1.180:5379")

.setPassword("a123456").setDatabase(0);

RedissonClientredissonClient2=Redisson.create(config2);

Configconfig3=newConfig();

config3.useSingleServer().setAddress("redis://172.29.1.180:5380")

.setPassword("a123456").setDatabase(0);

RedissonClientredissonClient3=Redisson.create(config3);

StringresourceName="REDLOCK";

RLocklock1=redissonClient1.getLock(resourceName);

RLocklock2=redissonClient2.getLock(resourceName);

RLocklock3=redissonClient3.getLock(resourceName);

RedissonRedLockredLock=newRedissonRedLock(lock1,lock2,lock3);

booleanisLock;

try{

isLock=redLock.tryLock(500,30000,TimeUnit.MILLISECONDS);

System.out.println("isLock="+isLock);

if(isLock){

//TODOifgetlocksuccess,dosomething;

Thread.sleep(30000);

}

}catch(Exceptione){

}finally{

//无论如何,最后都要解锁

System.out.println("");

redLock.unlock();

}

最核心的变化就是RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);,因为我这里是以三个节点为例。

那么如果是哨兵模式呢?需要搭建3个,或者5个sentinel模式集群(具体多少个,取决于你)。

那么如果是集群模式呢?需要搭建3个,或者5个cluster模式集群(具体多少个,取决于你)。

实现原理

既然核心变化是使用了RedissonRedLock,那么我们看一下它的源码有什么不同。这个类是RedissonMultiLock的子类,所以调用tryLock方法时,事实上调用了RedissonMultiLock的tryLock方法,精简源码如下:

publicbooleantryLock(longwaitTime,longleaseTime,TimeUnitunit)throwsInterruptedException{

//实现要点之允许加锁失败节点限制(N-(N/2+1))

intfailedLocksLimit=failedLocksLimit();

List<RLock>acquiredLocks=newArrayList<RLock>(locks.size());

//实现要点之遍历所有节点通过EVAL命令执行lua加锁

for(ListIterator<RLock>iterator=locks.listIterator();iterator.hasNext();){

RLocklock=iterator.next();

booleanlockAcquired;

try{

//对节点尝试加锁

lockAcquired=lock.tryLock(awaitTime,newLeaseTime,TimeUnit.MILLISECONDS);

}catch(RedisConnectionClosedException|RedisResponseTimeoutExceptione){

//如果抛出这类异常,为了防止加锁成功,但是响应失败,需要解锁

unlockInner(Arrays.asList(lock));

lockAcquired=false;

}catch(Exceptione){

//抛出异常表示获取锁失败

lockAcquired=false;

}

if(lockAcquired){

//成功获取锁集合

acquiredLocks.add(lock);

}else{

//如果达到了允许加锁失败节点限制,那么break,即此次Redlock加锁失败

if(locks.size()-acquiredLocks.size()==failedLocksLimit()){

break;

}

}

}

returntrue;

}

很明显,这段源码就是上一篇文章Redlock:Redis分布式锁的实现提到的Redlock算法的完全实现。

以sentinel模式架构为例,如下图所示,有sentinel-1,sentinel-2,sentinel-3总计3个sentinel模式集群,如果要获取分布式锁,那么需要向这3个sentinel集群通过EVAL命令执行LUA脚本,需要3/2+1=2,即至少2个sentinel集群响应成功,才算成功的以Redlock算法获取到分布式锁:

Redlock分布式锁

问题合集

image.png

根据上面实现原理的分析,这位同学应该是对Redlock算法实现有一点点误解,假设我们用5个节点实现Redlock算法的分布式锁。那么要么是5个redis单实例,要么是5个sentinel集群,要么是5个cluster集群。而不是一个有5个主节点的cluster集群,然后向每个节点通过EVAL命令执行LUA脚本尝试获取分布式锁,如上图所示。

失效时间如何设置

这个问题的场景是,假设设置失效时间10秒,如果由于某些原因导致10秒还没执行完任务,这时候锁自动失效,导致其他线程也会拿到分布式锁。

这确实是Redis分布式最大的问题,不管是普通分布式锁,还是Redlock算法分布式锁,都没有解决这个问题。也有一些文章提出了对失效时间续租,即延长失效时间,很明显这又提升了分布式锁的复杂度。另外就笔者了解,没有现成的框架有实现,如果有哪位知道,可以告诉我,万分感谢。

redis分布式锁的高可用

关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间已经发生过一场争论。有兴趣的同学,搜索"基于Redis的分布式锁到底安全吗"就能得到你想要的答案,需要注意的是,有上下两篇(这应该就是传说中的神仙打架吧,哈)。

zookeeper or redis

没有绝对的好坏,只有更适合自己的业务。就性能而言,redis很明显优于zookeeper;就分布式锁实现的健壮性而言,zookeeper很明显优于redis。如何选择,取决于你的业务!

号外:最近整理了之前编写的一系列内容做成了PDF,关注我并回复相应口令获取:

-001 :领取《Spring Boot基础教程》

- 002 :领取《Spring Cloud基础教程》

更多内容陆续奉上,敬请期待

- END -

近期热文:

为什么美国程序员工作比中国程序员工作轻松、加班少?

Spring Cloud Alibaba 基础教程整理

我为啥不看好ServiceMesh

敏捷团队的病与药

分布式系统关注点:弹性架构

GitHub 寻宝指南

虎牙直播在微服务改造方面的实践和总结

有赞搜索系统的架构演进

在前后端分离的路上承受了多少痛?

中台是个什么鬼?

看完,赶紧点个“好看”鸭

点鸭点鸭

↓↓↓↓

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。