zookeeper实现分布式锁-curator

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

2.Java封装基于ZK的分布式锁

2.1.创建项目导入依赖

org.springframework.boot

spring-boot-starter-parent

2.0.5.RELEASE

org.springframework.boot

spring-boot-starter-web

2.0.5.RELEASE

org.apache.zookeeper

zookeeper

3.4.9

org.projectlombok

lombok

1.18.12

provided

junit

junit

2.2.封装zookeeper分布式锁

/**======================================================================================

  • 方法描述:zookeeper分布式锁

  • 逻辑步骤:

实现 AutoCloseable 可自动关闭资源

Watcher观察器

======================================================================================*/

//zookeeper分布式锁

//实现 AutoCloseable 可自动关闭资源

//Watcher观察器

@Slf4j

public class ZookeeperLock implements AutoCloseable, Watcher {

//zookeeper客户端对象

private ZooKeeper zooKeeper;

//当前节点

private String currentNode;

//通过构造器初始化zookeeper

ZookeeperLock(){

try {

//参数:链接字符串 , 会话超时 , 监听器

this.zooKeeper = new ZooKeeper(“172.16.2.54:2181”,10000,this);

} catch (IOException e) {

e.printStackTrace();

}

}

/**======================================================================================

  • 方法描述:提供获取锁的方法: code作为业务相关的编码,比如传入订单ID,就锁住这个订单

  • 逻辑步骤:

1.创建根节点

2.进来一个线程,就为线程在zookeeper的根节点创建一个瞬时有序节点

======================================================================================*/

public boolean getLock(String code){

try {

//1.创建根节点,如果不存在,stat为null 就创建

String path = “/” + code;

Stat exists = zooKeeper.exists(path, false);

//创建节点

if(null == exists){

// 节点路径 ,节点值 ,权限:不需要账户密码就能链接 , 创建模式:顺序临时节点

zooKeeper.create(path,code.getBytes() , ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

}

//2.进来一个线程,就为线程在zookeeper创建一个瞬时有序节点 , 如: /xx/xx000000001 ;/xx/xx000000002

//把当前节点保存为成员变量,后面用来做判断

currentNode = zooKeeper.create(path+path,code.getBytes() , ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

//3.对所有的子节点进行排序

List children = zooKeeper.getChildren(path, false);

Collections.sort(children);

//4.如果排序过的children中的第一个和 currentNode 一致,拿到锁

String firstNode = children.get(0);

if(currentNode.endsWith(firstNode)){

return true;

}

//5.如果不是第一个节点,需要监听前一个节点

//用一个临时变量记录当前节点的上一个节点

String previousNode = firstNode;

for(String node : children){

if(currentNode.endsWith(node)){

//如果当前节点是node节点 ,那么就监听它的上一个节点 :比如 currentNode 这里是 0003节点 ,那 node就是 0002节点

//第一个参数是监听的节点,第二个参数是是否要监听,zooKeeper在初始化的时候设置好了监听器

log.info(“监听上一个节点:{}”,node);

zooKeeper.exists(path+“/”+previousNode,true);

}else{

//把children中的节点复制给上一个节点

previousNode = node;

}

}

//代码到这里,节点已经做好监听了,只需要等待,等待上一个节点完成工作后唤醒他

synchronized (this){

//wait会释放锁

wait();

}

//到这里说明被唤醒,说明获取到锁

log.info(“拿到锁:{}”,currentNode);

return true;

} catch (KeeperException e) {

e.printStackTrace();

} catch (InterruptedException e) {

e.printStackTrace();

}

return false;

}

@Override

public void close() {

//释放锁 :节点路径, 节点版本号(-1匹配所有版本)

log.info(“释放节点:{}”,currentNode);

if(null != currentNode){

try {

zooKeeper.delete(currentNode ,-1);

} catch (InterruptedException e) {

e.printStackTrace();

} catch (KeeperException e) {

e.printStackTrace();

}

}

}

@Override

public void process(WatchedEvent watchedEvent) {

//当节点被释放,就会到这里被监听到

if(watchedEvent.getType() == Event.EventType.NodeDeleted){

synchronized (this){

//唤醒等待的线程

log.info(“当前节点:{},唤醒”,watchedEvent.getPath());

notify();

}

}

}

}

2.3.测试代码

public class ZKTest {

@Test

public void testZK() throws Exception {

for(int i = 0 ; i < 10 ; i++){

new Thread(()->{

ZookeeperLock zookeeperLock = new ZookeeperLock();

boolean getLock = zookeeperLock.getLock(“order”);

System.out.println(“是否获取到锁:”+getLock);

zookeeperLock.close();

}).start();

}

Thread.sleep(5000);

}

}

打印日志:

09:18:24.500 [Thread-0] INFO cn.itsource.ZookeeperLock - /order/order0000000014监听上一个节点:/order/order0000000013

09:18:24.500 [Thread-9] INFO cn.itsource.ZookeeperLock - /order/order0000000017监听上一个节点:/order/order0000000016

09:18:24.500 [Thread-1] INFO cn.itsource.ZookeeperLock - /order/order0000000019监听上一个节点:/order/order0000000018

09:18:24.500 [Thread-8] INFO cn.itsource.ZookeeperLock - /order/order0000000018监听上一个节点:/order/order0000000017

09:18:24.500 [Thread-4] INFO cn.itsource.ZookeeperLock - /order/order0000000016监听上一个节点:/order/order0000000015

09:18:24.500 [Thread-2] INFO cn.itsource.ZookeeperLock - /order/order0000000020监听上一个节点:/order/order0000000019

09:18:24.500 [Thread-3] INFO cn.itsource.ZookeeperLock - /order/order0000000013监听上一个节点:/order/order0000000012

09:18:24.500 [Thread-7] INFO cn.itsource.ZookeeperLock - 释放节点:/order/order0000000011

09:18:24.500 [Thread-6] INFO cn.itsource.ZookeeperLock - /order/order0000000015监听上一个节点:/order/order0000000014

09:18:24.500 [Thread-5] INFO cn.itsource.ZookeeperLock - /order/order0000000012监听上一个节点:/order/order0000000011

09:18:24.509 [Thread-5-EventThread] INFO cn.itsource.ZookeeperLock - 当前节点:/order/order0000000011,唤醒

09:18:24.509 [Thread-5] INFO cn.itsource.ZookeeperLock - 拿到锁:/order/order0000000012

是否获取到锁:true

09:18:24.509 [Thread-5] INFO cn.itsource.ZookeeperLock - 释放节点:/order/order0000000012

09:18:24.511 [Thread-3-EventThread] INFO cn.itsource.ZookeeperLock - 当前节点:/order/order0000000012,唤醒

09:18:24.512 [Thread-3] INFO cn.itsource.ZookeeperLock - 拿到锁:/order/order0000000013

是否获取到锁:true

09:18:24.512 [Thread-3] INFO cn.itsource.ZookeeperLock - 释放节点:/order/order0000000013

09:18:24.514 [Thread-0-EventThread] INFO cn.itsource.ZookeeperLock - 当前节点:/order/order0000000013,唤醒

09:18:24.515 [Thread-0] INFO cn.itsource.ZookeeperLock - 拿到锁:/order/order0000000014

是否获取到锁:true

09:18:24.515 [Thread-0] INFO cn.itsource.ZookeeperLock - 释放节点:/order/order0000000014

09:18:24.517 [Thread-6-EventThread] INFO cn.itsource.ZookeeperLock - 当前节点:/order/order0000000014,唤醒

09:18:24.517 [Thread-6] INFO cn.itsource.ZookeeperLock - 拿到锁:/order/order0000000015

是否获取到锁:true

09:18:24.517 [Thread-6] INFO cn.itsource.ZookeeperLock - 释放节点:/order/order0000000015

09:18:24.520 [Thread-4-EventThread] INFO cn.itsource.ZookeeperLock - 当前节点:/order/order0000000015,唤醒

09:18:24.520 [Thread-4] INFO cn.itsource.ZookeeperLock - 拿到锁:/order/order0000000016

是否获取到锁:true

09:18:24.520 [Thread-4] INFO cn.itsource.ZookeeperLock - 释放节点:/order/order0000000016

09:18:24.522 [Thread-9] INFO cn.itsource.ZookeeperLock - 拿到锁:/order/order0000000017

是否获取到锁:true

09:18:24.522 [Thread-9] INFO cn.itsource.ZookeeperLock - 释放节点:/order/order0000000017

09:18:24.525 [Thread-8-EventThread] INFO cn.itsource.ZookeeperLock - 当前节点:/order/order0000000017,唤醒

09:18:24.525 [Thread-8] INFO cn.itsource.ZookeeperLock - 拿到锁:/order/order0000000018

是否获取到锁:true

09:18:24.525 [Thread-8] INFO cn.itsource.ZookeeperLock - 释放节点:/order/order0000000018

09:18:24.527 [Thread-1-EventThread] INFO cn.itsource.ZookeeperLock - 当前节点:/order/order0000000018,唤醒

09:18:24.527 [Thread-1] INFO cn.itsource.ZookeeperLock - 拿到锁:/order/order0000000019

是否获取到锁:true

09:18:24.527 [Thread-1] INFO cn.itsource.ZookeeperLock - 释放节点:/order/order0000000019

09:18:24.530 [Thread-2-EventThread] INFO cn.itsource.ZookeeperLock - 当前节点:/order/order0000000019,唤醒

09:18:24.530 [Thread-2] INFO cn.itsource.ZookeeperLock - 拿到锁:/order/order0000000020

是否获取到锁:true

09:18:24.530 [Thread-2] INFO cn.itsource.ZookeeperLock - 释放节点:/order/order0000000020

这个效果看起来是对的。但是如果自己基于zookeeper封装分布式锁未免太过麻烦,而且容易出BUG,Apache提供了一个基于Zookeeper的客户端工具curator已经实现了分布式锁的封装,我们使用它就可以了。

3.使用curator实现分布式锁

3.1.curator介绍

Apache Curator是用于Apache ZooKeeper(一种分布式协调服务)的Java / JVM客户端库。它包括一个高级API框架和实用程序,使使用Apache ZooKeeper变得更加轻松和可靠。解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连、反复注册Watcher和NodeExistsException异常等等。

重点是它对分布式锁进行了封装 :http://curator.apache.org/getting-started.html

3.1.导入依赖

org.apache.curator

curator-recipes

4.1.0

3.2.配置curator

//初始化方法start

@Bean(initMethod = “start”,destroyMethod = “close”)

public CuratorFramework curatorFramework(){

//重试策略

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);

//创建客户端

CuratorFramework client = CuratorFrameworkFactory.newClient(“172.16.2.54:2181”, retryPolicy);

return client;

}

3.3.分布式锁案例

@Slf4j

@RunWith(SpringRunner.class)

@SpringBootTest(classes = AppStart.class)

public class ZKTest {

@Autowired

private CuratorFramework curatorFramework;

/**======================================================================================

  • 方法描述:使用curator实现分布式锁

======================================================================================*/

@Test

public void testCurator() throws Exception {

for(int i = 0 ; i < 10 ; i++){

new Thread(()->{

testCuratorLock();

}).start();

}

Thread.sleep(5000);

}

public void testCuratorLock(){

//分布式锁

InterProcessMutex lock = new InterProcessMutex(curatorFramework, “/order”);

try {

if ( lock.acquire(1, TimeUnit.SECONDS) ){

//处理业务逻辑

log.info(“获取到锁”);

}

} catch (Exception e) {

e.printStackTrace();

} finally{

try {

//释放锁

log.info(“释放锁”);

lock.release();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

打印效果

2021-05-15 09:54:32.251 INFO 22488 — [ Thread-4] cn.itsource.ZKTest : 获取到锁

2021-05-15 09:54:32.252 INFO 22488 — [ Thread-4] cn.itsource.ZKTest : 释放锁

最后

分享一套我整理的面试干货,这份文档结合了我多年的面试官经验,站在面试官的角度来告诉你,面试官提的那些问题他最想听到你给他的回答是什么,分享出来帮助那些对前途感到迷茫的朋友。

面试经验技巧篇
  • 经验技巧1 如何巧妙地回答面试官的问题
  • 经验技巧2 如何回答技术性的问题
  • 经验技巧3 如何回答非技术性问题
  • 经验技巧4 如何回答快速估算类问题
  • 经验技巧5 如何回答算法设计问题
  • 经验技巧6 如何回答系统设计题
  • 经验技巧7 如何解决求职中的时间冲突问题
  • 经验技巧8 如果面试问题曾经遇见过,是否要告知面试官
  • 经验技巧9 在被企业拒绝后是否可以再申请
  • 经验技巧10 如何应对自己不会回答的问题
  • 经验技巧11 如何应对面试官的“激将法”语言
  • 经验技巧12 如何处理与面试官持不同观点这个问题
  • 经验技巧13 什么是职场暗语

面试真题篇
  • 真题详解1 某知名互联网下载服务提供商软件工程师笔试题
  • 真题详解2 某知名社交平台软件工程师笔试题
  • 真题详解3 某知名安全软件服务提供商软件工程师笔试题
  • 真题详解4 某知名互联网金融企业软件工程师笔试题
  • 真题详解5 某知名搜索引擎提供商软件工程师笔试题
  • 真题详解6 某初创公司软件工程师笔试题
  • 真题详解7 某知名游戏软件开发公司软件工程师笔试题
  • 真题详解8 某知名电子商务公司软件工程师笔试题
  • 真题详解9 某顶级生活消费类网站软件工程师笔试题
  • 真题详解10 某知名门户网站软件工程师笔试题
  • 真题详解11 某知名互联网金融企业软件工程师笔试题
  • 真题详解12 国内某知名网络设备提供商软件工程师笔试题
  • 真题详解13 国内某顶级手机制造商软件工程师笔试题
  • 真题详解14 某顶级大数据综合服务提供商软件工程师笔试题
  • 真题详解15 某著名社交类上市公司软件工程师笔试题
  • 真题详解16 某知名互联网公司软件工程师笔试题
  • 真题详解17 某知名网络安全公司校园招聘技术类笔试题
  • 真题详解18 某知名互联网游戏公司校园招聘运维开发岗笔试题

资料整理不易,点个关注再走吧
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
thU2zKk-1714772398363)]

面试真题篇
  • 真题详解1 某知名互联网下载服务提供商软件工程师笔试题
  • 真题详解2 某知名社交平台软件工程师笔试题
  • 真题详解3 某知名安全软件服务提供商软件工程师笔试题
  • 真题详解4 某知名互联网金融企业软件工程师笔试题
  • 真题详解5 某知名搜索引擎提供商软件工程师笔试题
  • 真题详解6 某初创公司软件工程师笔试题
  • 真题详解7 某知名游戏软件开发公司软件工程师笔试题
  • 真题详解8 某知名电子商务公司软件工程师笔试题
  • 真题详解9 某顶级生活消费类网站软件工程师笔试题
  • 真题详解10 某知名门户网站软件工程师笔试题
  • 真题详解11 某知名互联网金融企业软件工程师笔试题
  • 真题详解12 国内某知名网络设备提供商软件工程师笔试题
  • 真题详解13 国内某顶级手机制造商软件工程师笔试题
  • 真题详解14 某顶级大数据综合服务提供商软件工程师笔试题
  • 真题详解15 某著名社交类上市公司软件工程师笔试题
  • 真题详解16 某知名互联网公司软件工程师笔试题
  • 真题详解17 某知名网络安全公司校园招聘技术类笔试题
  • 真题详解18 某知名互联网游戏公司校园招聘运维开发岗笔试题

[外链图片转存中…(img-qpzwGn2x-1714772398364)]

资料整理不易,点个关注再走吧
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值