Curator实现分布式锁
分布式锁的应用
分布式锁服务宕机, ZooKeeper 一般是以集群部署, 如果出现 ZooKeeper 宕机, 那么只要当前正常的服务器超过集群的半数, 依然可以正常提供服务
持有锁资源服务器宕机, 假如一台服务器获取锁之后就宕机了, 那么就会导致其他服务器无法再获取该锁. 就会造成 死锁 问题, 在 Curator 中, 锁的信息都是保存在临时节点上, 如果持有锁资源的服务器宕机, 那么 ZooKeeper 就会移除它的信息, 这时其他服务器就能进行获取锁操作。
当然了分布式锁还可以基于redis实现,其string类型的 setnx key value命令 结合expire命令。
前面的准备工作:
package Lock; import java.util.concurrent.TimeUnit; import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.CloseableUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test; public class DistributedLockDemo {
// ZooKeeper 锁节点路径, 分布式锁的相关操作都是在这个节点上进行
private final String lockPath = "/distributed-lock"; // ZooKeeper 服务地址, 单机格式为:(127.0.0.1:2181),
// 集群格式为:(127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183)
private String connectString;
// Curator 客户端重试策略
private RetryPolicy retry;
// Curator 客户端对象
private CuratorFramework client;
// client2 用户模拟其他客户端
private CuratorFramework client2; // 初始化资源
@Before
public void init() throws Exception {
// 设置 ZooKeeper 服务地址为本机的 2181 端口
connectString = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";
// 重试策略
// 初始休眠时间为 1000ms, 最大重试次数为 3
retry = new ExponentialBackoffRetry(1000, 3);
// 创建一个客户端, 60000(ms)为 session 超时时间, 15000(ms)为链接超时时间
client = CuratorFrameworkFactory.newClient(connectString, 60000, 15000, retry);
client2 = CuratorFrameworkFactory.newClient(connectString, 60000, 15000, retry);
// 创建会话
client.start();
client2.start();
} // 释放资源
@After
public void close() {
CloseableUtils.closeQuietly(client);
}
}
zookeper的实现主要有下面四类类:
InterProcessMutex:分布式可重入排它锁
InterProcessSemaphoreMutex:分布式排它锁
InterProcessReadWriteLock:分布式读写锁
InterProcessMultiLock:将多个锁作为单个实体管理的容器
1.共享锁,不可重入--- InterProcessSemaphoreMutex
InterProcessSemaphoreMutex是一种不可重入的互斥锁,也就意味着即使是同一个线程也无法在持有锁的情况下再次获得锁,所以需要注意,不可重入的锁很容易在一些情况导致死锁。
@Test
public void sharedLock() throws Exception {
// 创建共享锁
final InterProcessLock lock = new InterProcessSemaphoreMutex(client, lockPath);
// lock2 用于模拟其他客户端
final InterProcessLock lock2 = new InterProcessSemaphoreMutex(client2, lockPath); new Thread(new Runnable() {
@Override
public void run() {
// 获取锁对象
try {
lock.acquire();
System.out.println("1获取锁===============");
// 测试锁重入
Thread.sleep(5 * 1000);
lock.release();
System.out.println("1释放锁===============");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
// 获取锁对象
try {
lock2.acquire();
System.out.println("2获取锁===============");
Thread.sleep(5 * 1000);
lock2.release();
System.out.println("2释放锁===============");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); Thread.sleep(20 * 1000);
}
2.共享可重入锁--- InterProcessMutex
此锁可以重入,但是重入几次需要释放几次。
@Test
public void sharedReentrantLock() throws Exception {
// 创建共享锁
final InterProcessLock lock = new InterProcessMutex(client, lockPath);
// lock2 用于模拟其他客户端
final InterProcessLock lock2 = new InterProcessMutex(client2, lockPath); final CountDownLatch countDownLatch = new CountDownLatch(2); new Thread(new Runnable() {
@Override
public void run() {
// 获取锁对象
try {
lock.acquire();
System.out.println("1获取锁===============");
// 测试锁重入
lock.acquire();
System.out.println("1再次获取锁===============");
Thread.sleep(5 * 1000);
lock.release();
System.out.println("1释放锁===============");
lock.release();
System.out.println("1再次释放锁==============="); countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
// 获取锁对象
try {
lock2.acquire();
System.out.println("2获取锁===============");
// 测试锁重入
lock2.acquire();
System.out.println("2再次获取锁===============");
Thread.sleep(5 * 1000);
lock2.release();
System.out.println("2释放锁===============");
lock2.release();
System.out.println("2再次释放锁==============="); countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); countDownLatch.await();
}
结果:
1获取锁===============
1再次获取锁===============
1释放锁===============
1再次释放锁===============
2获取锁===============
2再次获取锁===============
2释放锁===============
2再次释放锁===============
原理:
InterProcessMutex通过在zookeeper的某路径节点下创建临时序列节点来实现分布式锁,即每个线程(跨进程的线程)获取同一把锁前,都需要在同样的路径下创建一个节点,节点名字由uuid + 递增序列组成。而通过对比自身的序列数是否在所有子节点的第一位,来判断是否成功获取到了锁。当获取锁失败时,它会添加watcher来监听前一个节点的变动情况,然后进行等待状态。直到watcher的事件生效将自己唤醒,或者超时时间异常返回。
3.共享可重入读写锁
读锁和读锁不互斥,只要有写锁就互斥。
@Test
public void sharedReentrantReadWriteLock() throws Exception {
// 创建共享可重入读写锁
final InterProcessReadWriteLock locl1 = new InterProcessReadWriteLock(client, lockPath);
// lock2 用于模拟其他客户端
final InterProcessReadWriteLock lock2 = new InterProcessReadWriteLock(client2, lockPath); // 获取读写锁(使用 InterProcessMutex 实现, 所以是可以重入的)
final InterProcessLock readLock = locl1.readLock();
final InterProcessLock readLockw = lock2.readLock(); final CountDownLatch countDownLatch = new CountDownLatch(2); new Thread(new Runnable() {
@Override
public void run() {
// 获取锁对象
try {
readLock.acquire();
System.out.println("1获取读锁===============");
// 测试锁重入
readLock.acquire();
System.out.println("1再次获取读锁===============");
Thread.sleep(5 * 1000);
readLock.release();
System.out.println("1释放读锁===============");
readLock.release();
System.out.println("1再次释放读锁==============="); countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
// 获取锁对象
try {
Thread.sleep(500);
readLockw.acquire();
System.out.println("2获取读锁===============");
// 测试锁重入
readLockw.acquire();
System.out.println("2再次获取读锁==============");
Thread.sleep(5 * 1000);
readLockw.release();
System.out.println("2释放读锁===============");
readLockw.release();
System.out.println("2再次释放读锁==============="); countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); countDownLatch.await();
}
结果:
1获取读锁===============
1再次获取读锁===============
2获取读锁===============
2再次获取读锁==============
1释放读锁===============
2释放读锁===============
1再次释放读锁===============
2再次释放读锁===============
4. 共享信号量
@Test
public void semaphore() throws Exception {
// 创建一个信号量, Curator 以公平锁的方式进行实现
final InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, lockPath, 1); final CountDownLatch countDownLatch = new CountDownLatch(2); new Thread(new Runnable() {
@Override
public void run() {
// 获取锁对象
try {
// 获取一个许可
Lease lease = semaphore.acquire();
logger.info("1获取读信号量===============");
Thread.sleep(5 * 1000);
semaphore.returnLease(lease);
logger.info("1释放读信号量==============="); countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
// 获取锁对象
try {
// 获取一个许可
Lease lease = semaphore.acquire();
logger.info("2获取读信号量===============");
Thread.sleep(5 * 1000);
semaphore.returnLease(lease);
logger.info("2释放读信号量==============="); countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); countDownLatch.await();
}
结果:
09:39:26 [Lock.DistributedLockDemo]-[INFO] 2获取读信号量===============
09:39:32 [Lock.DistributedLockDemo]-[INFO] 2释放读信号量===============
09:39:32 [Lock.DistributedLockDemo]-[INFO] 1获取读信号量===============
09:39:37 [Lock.DistributedLockDemo]-[INFO] 1释放读信号量===============
当然可以一次获取多个信号量:
@Test
public void semaphore() throws Exception {
// 创建一个信号量, Curator 以公平锁的方式进行实现
final InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, lockPath, 3); final CountDownLatch countDownLatch = new CountDownLatch(2); new Thread(new Runnable() {
@Override
public void run() {
// 获取锁对象
try {
// 获取2个许可
Collection<Lease> acquire = semaphore.acquire(2);
logger.info("1获取读信号量===============");
Thread.sleep(5 * 1000);
semaphore.returnAll(acquire);
logger.info("1释放读信号量==============="); countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
// 获取锁对象
try {
// 获取1个许可
Collection<Lease> acquire = semaphore.acquire(1);
logger.info("2获取读信号量===============");
Thread.sleep(5 * 1000);
semaphore.returnAll(acquire);
logger.info("2释放读信号量==============="); countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); countDownLatch.await();
}
结果:
09:46:53 [Lock.DistributedLockDemo]-[INFO] 2获取读信号量===============
09:46:53 [Lock.DistributedLockDemo]-[INFO] 1获取读信号量===============
09:46:58 [Lock.DistributedLockDemo]-[INFO] 2释放读信号量===============
09:46:58 [Lock.DistributedLockDemo]-[INFO] 1释放读信号量===============
5.多重共享锁
@Test
public void multiLock() throws Exception {
// 可重入锁
final InterProcessLock interProcessLock1 = new InterProcessMutex(client, lockPath);
// 不可重入锁
final InterProcessLock interProcessLock2 = new InterProcessSemaphoreMutex(client2, lockPath);
// 创建多重锁对象
final InterProcessLock lock = new InterProcessMultiLock(Arrays.asList(interProcessLock1, interProcessLock2)); final CountDownLatch countDownLatch = new CountDownLatch(1); new Thread(new Runnable() {
@Override
public void run() {
// 获取锁对象
try {
// 获取参数集合中的所有锁
lock.acquire();
// 因为存在一个不可重入锁, 所以整个 InterProcessMultiLock 不可重入
System.out.println(lock.acquire(2, TimeUnit.SECONDS));
// interProcessLock1 是可重入锁, 所以可以继续获取锁
System.out.println(interProcessLock1.acquire(2, TimeUnit.SECONDS));
// interProcessLock2 是不可重入锁, 所以获取锁失败
System.out.println(interProcessLock2.acquire(2, TimeUnit.SECONDS)); countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); countDownLatch.await();
}
结果:
false
true
false
Curator实现分布式锁的更多相关文章
- Curator Zookeeper分布式锁
Curator Zookeeper分布式锁 pom.xml中添加如下配置 <!-- https://mvnrepository.com/artifact/org.apache.curator/c ...
- springboot整合curator实现分布式锁
理论篇: Curator是Netflix开源的一套ZooKeeper客户端框架. Netflix在使用ZooKeeper的过程中发现ZooKeeper自带的客户端太底层, 应用方在使用的时候需要自己处 ...
- ZooKeeper(八)-- Curator实现分布式锁
1.pom.xml <dependencies> <dependency> <groupId>junit</groupId> <artifactI ...
- spring整合curator实现分布式锁
为什么要有分布式锁? 比如说,我们要下单,分为两个操作,下单成功(订单服务),扣减库存(商品服务).如果没有锁的话,同时两个请求进来.先检查有没有库存,一看都有,然后下订单,减库存.这时候肯定会出现错 ...
- zoolkeeper 的Curator的分布式锁
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3) CuratorFramework client = CuratorFram ...
- 女朋友也能看懂的Zookeeper分布式锁原理
前言 关于分布式锁,在互联网行业的使用场景还是比较多的,比如电商的库存扣减,秒杀活动,集群定时任务执行等需要进程互斥的场景.而实现分布式锁的手段也很多,大家比较常见的就是redis跟zookeep ...
- zookeeper分布式锁和服务优化配置
转自:https://www.jianshu.com/p/02eeaee4357f?utm_campaign=maleskine&utm_content=note&utm_medium ...
- java 分布式锁 -图解- 秒懂
目录 写在前面 1.1. 分布式锁 简介 1.1.1. 图解:公平锁和可重入锁 模型 1.1.2. 图解: zookeeper分布式锁的原理 1.1.3. 分布式锁的基本流程 1.1.4. 加锁的实现 ...
- SpringBoot电商项目实战 — Zookeeper的分布式锁实现
上一篇演示了基于Redis的Redisson分布式锁实现,那今天我要再来说说基于Zookeeper的分布式现实. Zookeeper分布式锁实现 要用Zookeeper实现分布式锁,我就不得不说说zo ...
随机推荐
- Qt ------ linux(ubuntu) 下用 linuxdeployqt 打包发布程序
https://github.com/probonopd/linuxdeployqt/releases 1.下载linuxdeployqt 2.把下载的文件放入 /usr/local/bin 内,并执 ...
- 转载:C++ 二维数组new
来源:http://developer.51cto.com/art/201002/183127.htm C++编程语言中有一种叫做new的二维数组,它的应用方式比较灵活,可以有多种方法来帮助我们实现一 ...
- layui基础总结
1.layui结构 ├─css //css目录 │ │─modules //模块css目录(一般如果模块相对较大,我们会单独提取,比如下面三个:) │ │ ├─laydate ...
- 2Servlet笔记
1.静态页面(html) 2.动态 用户可以输入数据,和页面交互. 3 BS和CS的区别 BS :Browser Server(WEB 服务器) CS : Client(qq tcp/ip协议) ...
- UVA11527Unique Snowflakes(滑动窗口 + set判重 | | map)
题意:输入长度为n的序列a,找到一个尽量长的连续子序列a[l] - a[r],使该序列中没有相同的元素 紫薯P239 序列元素从0开始编号,l 和 r 分别表示子序列左右端点,初始化为0,固定 l,判 ...
- Golang的排序和查找
Golang的排序和查找 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.排序的基本介绍 排序是将一组数据,依指定的顺序进行排列的过程.排序的分类如下 1>.内部排序 指将 ...
- JAVA核心技术I---JAVA基础知识(常量设计和常量池)
一:常量---一种不会修改的变量 –Java没有constant关键字 –不能修改,final –不会修改/只读/只要一份,static –方便访问publicJava中的常量 –public sta ...
- 设计模式---数据结构模式之组合模式(Composite)
前提:数据结构模式 常常有一些组建在内部具有特定的数据结构,如果让客户程序依赖这些特定的数据结构,将极大的破坏组件的复用.这时候,将这些数据结构封装在内部,在外部提供统一的接口,来实现与特定数据结构无 ...
- Mac下显示网页全屏快捷键
control+command+F mac下谷歌浏览器全屏时隐藏头部:(隐藏标签页和地址栏) command+shift+B
- HDU - 3974 Assign the task (线段树区间修改+构建模型)
https://cn.vjudge.net/problem/HDU-3974 题意 有一棵树,给一个结点分配任务时,其子树的所有结点都能接受到此任务.有两个操作,C x表示查询x结点此时任务编号,T ...