Redisson实现Redis分布式锁的N种姿势
前几天发的一篇文章《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
-
Config config = new Config();
-
config.useSingleServer().setAddress("redis://172.29.1.180:5379").setPassword("a123456").setDatabase(0);
-
// 构造RedissonClient
-
RedissonClient redissonClient = Redisson.create(config);
-
// 设置锁定资源名称
-
RLock disLock = redissonClient.getLock("DISLOCK");
-
boolean isLock;
-
try {
-
//尝试获取分布式锁
-
isLock = disLock.tryLock(500, 15000, TimeUnit.MILLISECONDS);
-
if (isLock) {
-
//TODO if get lock success, do something;
-
Thread.sleep(15000);
-
}
-
} catch (Exception e) {
-
} finally {
-
// 无论如何, 最后都要解锁
-
disLock.unlock();
-
}
通过代码可知,经过Redisson的封装,实现Redis分布式锁非常方便,我们再看一下Redis中的value是啥,和前文分析一样,hash结构,key就是资源名称,field就是UUID+threadId,value就是重入值,在分布式锁时,这个值为1(Redisson还可以实现重入锁,那么这个值就取决于重入次数了):
-
172.29.1.180:5379> hgetall DISLOCK
-
1) "01a6d806-d282-4715-9bec-f51b9aa98110:1"
-
2) "1"
哨兵模式
即sentinel模式,实现代码和单机模式几乎一样,唯一的不同就是Config的构造:
-
Config config = new Config();
-
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如下:
-
Config config = new Config();
-
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架构为例,直接看实现代码:
-
Config config1 = new Config();
-
config1.useSingleServer().setAddress("redis://172.29.1.180:5378")
-
.setPassword("a123456").setDatabase(0);
-
RedissonClient redissonClient1 = Redisson.create(config1);
-
Config config2 = new Config();
-
config2.useSingleServer().setAddress("redis://172.29.1.180:5379")
-
.setPassword("a123456").setDatabase(0);
-
RedissonClient redissonClient2 = Redisson.create(config2);
-
Config config3 = new Config();
-
config3.useSingleServer().setAddress("redis://172.29.1.180:5380")
-
.setPassword("a123456").setDatabase(0);
-
RedissonClient redissonClient3 = Redisson.create(config3);
-
String resourceName = "REDLOCK";
-
RLock lock1 = redissonClient1.getLock(resourceName);
-
RLock lock2 = redissonClient2.getLock(resourceName);
-
RLock lock3 = redissonClient3.getLock(resourceName);
-
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
-
boolean isLock;
-
try {
-
isLock = redLock.tryLock(500, 30000, TimeUnit.MILLISECONDS);
-
System.out.println("isLock = "+isLock);
-
if (isLock) {
-
//TODO if get lock success, do something;
-
Thread.sleep(30000);
-
}
-
} catch (Exception e) {
-
} finally {
-
// 无论如何, 最后都要解锁
-
System.out.println("");
-
redLock.unlock();
-
}
最核心的变化就是 RedissonRedLockredLock=newRedissonRedLock(lock1,lock2,lock3);
,因为我这里是以三个节点为例。
那么如果是哨兵模式呢?需要搭建3个,或者5个sentinel模式集群(具体多少个,取决于你)。 那么如果是集群模式呢?需要搭建3个,或者5个cluster模式集群(具体多少个,取决于你)。
实现原理
既然核心变化是使用了RedissonRedLock,那么我们看一下它的源码有什么不同。这个类是RedissonMultiLock的子类,所以调用tryLock方法时,事实上调用了RedissonMultiLock的tryLock方法,精简源码如下:
-
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
-
// 实现要点之允许加锁失败节点限制(N-(N/2+1))
-
int failedLocksLimit = failedLocksLimit();
-
List<RLock> acquiredLocks = new ArrayList<RLock>(locks.size());
-
// 实现要点之遍历所有节点通过EVAL命令执行lua加锁
-
for (ListIterator<RLock> iterator = locks.listIterator(); iterator.hasNext();) {
-
RLock lock = iterator.next();
-
boolean lockAcquired;
-
try {
-
// 对节点尝试加锁
-
lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);
-
} catch (RedisConnectionClosedException|RedisResponseTimeoutException e) {
-
// 如果抛出这类异常,为了防止加锁成功,但是响应失败,需要解锁
-
unlockInner(Arrays.asList(lock));
-
lockAcquired = false;
-
} catch (Exception e) {
-
// 抛出异常表示获取锁失败
-
lockAcquired = false;
-
}
-
if (lockAcquired) {
-
// 成功获取锁集合
-
acquiredLocks.add(lock);
-
} else {
-
// 如果达到了允许加锁失败节点限制,那么break,即此次Redlock加锁失败
-
if (locks.size() - acquiredLocks.size() == failedLocksLimit()) {
-
break;
-
}
-
}
-
}
-
return true;
-
}
很明显,这段源码就是上一篇文章《Redlock:Redis分布式锁最牛逼的实现》提到的Redlock算法的完全实现。
以sentinel模式架构为例,如下图所示,有sentinel-1,sentinel-2,sentinel-3总计3个sentinel模式集群,如果要获取分布式锁,那么需要向这3个sentinel集群通过EVAL命令执行LUA脚本,需要3/2+1=2,即至少2个sentinel集群响应成功,才算成功的以Redlock算法获取到分布式锁:
问题合集
失效时间如何设置
这个问题的场景是,假设设置失效时间10秒,如果由于某些原因导致10秒还没执行完任务,这时候锁自动失效,导致其他线程也会拿到分布式锁。
这确实是Redis分布式最大的问题,不管是普通分布式锁,还是Redlock算法分布式锁,都没有解决这个问题。也有一些文章提出了对失效时间续租,即延长失效时间,很明显这又提升了分布式锁的复杂度。另外就笔者了解,没有现成的框架有实现,如果有哪位知道,可以告诉我,万分感谢。
redis分布式锁的高可用
关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间已经发生过一场争论。有兴趣的同学,搜索"基于Redis的分布式锁到底安全吗"就能得到你想要的答案,需要注意的是,有上下两篇(这应该就是传说中的神仙打架吧,哈)。
zookeeper or redis
没有绝对的好坏,只有更适合自己的业务。就性能而言,redis很明显优于zookeeper;就分布式锁实现的健壮性而言,zookeeper很明显优于redis。如何选择,取决于你的业务!
原文发布时间为: 2018-12-03
本文作者:阿飞的博客
本文来自云栖社区合作伙伴“ Java技术驿站 ”,了解相关信息可以关注“ Java技术驿站 ”。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
五年Android工作总结整理的所有面试资源
本文陆陆续续两个星期整理出来的,所有面试资源,均是本人在长期学习的过程中汇总,并以笔记形式记录下来的。看完本文,如果你有需要,尽管拿走好了~ 获取方式详见文末。 概述 前段时间在准备面试的东西,整理了一些读书笔记分享给各位,大家可以自由下载,以下内容完全原创。 前两部分是对于一些 经典书籍的读书笔记和面试题,都是上学看书的时候觉得比较重要的部分,就通过Word文档的形式记录下来了。因为一直从事的是Android方面的相关工作,因此还是围绕着Android展开,包括了Java、C++、网络、数据结构等等。 最后一部分是面试时候的算法题,根据问题的类型分为了数组、数字、字符串、图、树等等。 先扯点题外话,这些基础知识虽然很久没看了,不过幸亏有这些笔记,现在看起来很快,像C++ Primer和Java面向对象编程这样很厚的书,通过看这些笔记两个小时就能复习完了。因此也建议大家平时看书的时候,多用文档和博客的形式记录下来,对于未来很有用。 我平时在学习的时候,一直秉承着要建立一个完整的架构体系,就像今年定的目标,要把之前五年的工作都总结一下,梳理成一个完整的框架,所以才有了 Android 知...
- 下一篇
phpcms网站漏洞修复 远程代码写入缓存漏洞利用
SINE安全公司在对phpcms2008网站代码进行安全检测与审计的时候发现该phpcms存在远程代码写入缓存文件的一个SQL注入漏洞,该phpcms漏洞危害较大,可以导致网站被黑,以及服务器遭受黑客的攻击,关于这次发现的phpcms漏洞细节以及如何利用提权我们来详细剖析。 phpcms2008是国内深受站长建站使用的一个内容CMS管理系统,phpcms的开源话,免费,动态,静态生成,API接口,模板免费下载,自定义内容设计,可提供程序的二次开发与设计,大大方便了整个互联网站长的建站使用与优化。整个phpcms采用PHP+Mysql数据库作为架构,稳定,并发高,承载量大。 phpcms2008漏洞详情 在对代码的安全检测与审计当中,发现type.php文件代码存在漏洞,代码如下: <?php require dirname(__FILE__).'/include/common.inc.php'; ... if(empty($template)) $template = 'type'; ... include template('phpcms', $template); ... ?...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Linux系统CentOS6、CentOS7手动修改IP地址
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS7安装Docker,走上虚拟化容器引擎之路
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Red5直播服务器,属于Java语言的直播服务器
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库