1. 什么是分布式锁
分布式锁主要用于在分布式环境中保护跨进程、跨主机、跨网络的共享资源实现互斥访问,以达到保证数据的一致性。,区别于单实例部署的场景,单实例部署只需要使用synchronized,Reenterlock实现,分布式场景需要借助中间件实现。
2. redis实现
(1)利用 setnx (set not exist),某个键不存在的时候才会成功
// example 1
if(setnx(key,1) == 1){
expire(key,30)
try {
do something ......
} finally {
del(key)
}
}
不足:如果setnx后此节点崩了,则锁永远被占用
(2) 替换成 set(key,1,30,NX)让设置和过期原子化
// example 2
if(set(key,1,30,NX) == 1){
try {
do something ......
} finally {
del(key)
}
}
不足:多个进程可能导致误删
=> X获取lockA
=> lockA 过期(X操作未执行完)
=> Y获取lockA
=> X操作完,执行del lockA ,此时X 自己获取的那个锁已经过期了,删掉的是Y获取的锁
=> 其他线程进来,发现没有锁,直接获取,锁被破坏
(3) 使用守护进程为锁续命
创建锁的时候,同时创建一个守护进程,每10s为锁重新设置一次过期时间,任务执行完后删除守护进程,则不再继续续命,同时删掉锁,释放资源。
看程序员小灰公众号
3. redission 实现
如果负责储存某些分布式锁的某些Redis节点宕机以后,而且这些锁正好处于锁住的状态时,这些锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。
另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
public void testReentrantLock(RedissonClient redisson){
RLock lock = redisson.getLock("anyLock");
try{
// 1. 最常见的使用方法
//lock.lock();
// 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
//lock.lock(10, TimeUnit.SECONDS);
// 3. 尝试加锁,最多等待3秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(3, 10, TimeUnit.SECONDS);
if(res){ //成功
// do your business
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
4. zookeeper实现
参考:https://blog.csdn.net/u011191463/article/details/78658036
不好的方案:
对于一个锁都有一个节点locker,所有client监听这个locker的子节点,如果子节点被删掉则说明锁被释放,有子节点则说明锁被占用。
缺点:
所有client都监听locker,locker的任何动作都会惊动所有client,但是最终只有一个client会获得锁,这就是所谓的惊群。
为了解决惊群的问题,采用下面的方案。
原理:
利用zk的临时顺序节点,首先创建一个locker节点,如果有线程要访问,则创建一个临时顺序节点node1,判断node1是否第一个节点,是的话就执行,不是则监听前一个节点,等前一个节点删除后再执行。
锁的获取过程
Curator 实现:
Curator是Netflix公司开源的一套zookeeper客户端框架,解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连、反复注册Watcher和NodeExistsException异常等等。
Curator实现了大部分锁,重入锁,不可重入锁,读锁,写锁,信号量等等。
<!-- curator 客户端 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
String connectionInfo = "100.69.12.9:2881,100.69.12.9:2882";
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client =
CuratorFrameworkFactory.builder()
.connectString(connectionInfo)
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(retryPolicy)
.build();
client.start();
InterProcessMutex lock = new InterProcessMutex(client,"/path/lock");
try {
// 获得一个锁
lock.acquire();
// 获得一个锁,并设置过期时间
lock.acquire(5, TimeUnit.MICROSECONDS);
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
lock.release();
} catch (Exception e) {
e.printStackTrace();
}
}