引言

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

ZooKeeper的架构通过冗余服务实现高可用性。因此,如果第一次无应答,客户端就可以询问另一台ZooKeeper主机。ZooKeeper节点将它们的数据存储于一个分层的命名空间,非常类似于一个文件系统或一个前缀树结构。客户端可以在节点读写,从而以这种方式拥有一个共享的配置服务。更新是全序的。

基于ZooKeeper分布式锁的流程

  • 在zookeeper指定节点(locks)下创建临时顺序节点node_n
  • 获取locks下所有子节点children
  • 对子节点按节点自增序号从小到大排序
  • 判断本节点是不是第一个子节点,若是,则获取锁;若不是,则监听比该节点小的那个节点的删除事件
  • 若监听事件生效,则回到第二步重新进行判断,直到获取到锁

具体实现

下面就具体使用java和zookeeper实现分布式锁,操作zookeeper使用的是apache提供的zookeeper的包。

  • 通过实现Watch接口,实现process(WatchedEvent event)方法来实施监控,使CountDownLatch来完成监控,在等待锁的时候使用CountDownLatch来计数,等到后进行countDown,停止等待,继续运行。
  • 以下整体流程基本与上述描述流程一致,只是在监听的时候使用的是CountDownLatch来监听前一个节点。

分布式锁

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock; /**
* Created by liuyang on 2017/4/20.
*/
public class DistributedLock implements Lock, Watcher {
private ZooKeeper zk = null;
// 根节点
private String ROOT_LOCK = "/locks";
// 竞争的资源
private String lockName;
// 等待的前一个锁
private String WAIT_LOCK;
// 当前锁
private String CURRENT_LOCK;
// 计数器
private CountDownLatch countDownLatch;
private int sessionTimeout = 30000;
private List<Exception> exceptionList = new ArrayList<Exception>(); /**
* 配置分布式锁
* @param config 连接的url
* @param lockName 竞争资源
*/
public DistributedLock(String config, String lockName) {
this.lockName = lockName;
try {
// 连接zookeeper
zk = new ZooKeeper(config, sessionTimeout, this);
Stat stat = zk.exists(ROOT_LOCK, false);
if (stat == null) {
// 如果根节点不存在,则创建根节点
zk.create(ROOT_LOCK, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
} // 节点监视器
public void process(WatchedEvent event) {
if (this.countDownLatch != null) {
this.countDownLatch.countDown();
}
} public void lock() {
if (exceptionList.size() > 0) {
throw new LockException(exceptionList.get(0));
}
try {
if (this.tryLock()) {
System.out.println(Thread.currentThread().getName() + " " + lockName + "获得了锁");
return;
} else {
// 等待锁
waitForLock(WAIT_LOCK, sessionTimeout);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
} public boolean tryLock() {
try {
String splitStr = "_lock_";
if (lockName.contains(splitStr)) {
throw new LockException("锁名有误");
}
// 创建临时有序节点
CURRENT_LOCK = zk.create(ROOT_LOCK + "/" + lockName + splitStr, new byte[0],
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(CURRENT_LOCK + " 已经创建");
// 取所有子节点
List<String> subNodes = zk.getChildren(ROOT_LOCK, false);
// 取出所有lockName的锁
List<String> lockObjects = new ArrayList<String>();
for (String node : subNodes) {
String _node = node.split(splitStr)[0];
if (_node.equals(lockName)) {
lockObjects.add(node);
}
}
Collections.sort(lockObjects);
System.out.println(Thread.currentThread().getName() + " 的锁是 " + CURRENT_LOCK);
// 若当前节点为最小节点,则获取锁成功
if (CURRENT_LOCK.equals(ROOT_LOCK + "/" + lockObjects.get(0))) {
return true;
} // 若不是最小节点,则找到自己的前一个节点
String prevNode = CURRENT_LOCK.substring(CURRENT_LOCK.lastIndexOf("/") + 1);
WAIT_LOCK = lockObjects.get(Collections.binarySearch(lockObjects, prevNode) - 1);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
return false;
} public boolean tryLock(long timeout, TimeUnit unit) {
try {
if (this.tryLock()) {
return true;
}
return waitForLock(WAIT_LOCK, timeout);
} catch (Exception e) {
e.printStackTrace();
}
return false;
} // 等待锁
private boolean waitForLock(String prev, long waitTime) throws KeeperException, InterruptedException {
Stat stat = zk.exists(ROOT_LOCK + "/" + prev, true); if (stat != null) {
System.out.println(Thread.currentThread().getName() + "等待锁 " + ROOT_LOCK + "/" + prev);
this.countDownLatch = new CountDownLatch(1);
// 计数等待,若等到前一个节点消失,则precess中进行countDown,停止等待,获取锁
this.countDownLatch.await(waitTime, TimeUnit.MILLISECONDS);
this.countDownLatch = null;
System.out.println(Thread.currentThread().getName() + " 等到了锁");
}
return true;
} public void unlock() {
try {
System.out.println("释放锁 " + CURRENT_LOCK);
zk.delete(CURRENT_LOCK, -1);
CURRENT_LOCK = null;
zk.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
} public Condition newCondition() {
return null;
} public void lockInterruptibly() throws InterruptedException {
this.lock();
} public class LockException extends RuntimeException {
private static final long serialVersionUID = 1L;
public LockException(String e){
super(e);
}
public LockException(Exception e){
super(e);
}
}
}
测试代码
public class Test {
static int n = 500; public static void secskill() {
System.out.println(--n);
} public static void main(String[] args) { Runnable runnable = new Runnable() {
public void run() {
DistributedLock lock = null;
try {
lock = new DistributedLock("127.0.0.1:2181", "test1");
lock.lock();
secskill();
System.out.println(Thread.currentThread().getName() + "正在运行");
} finally {
if (lock != null) {
lock.unlock();
}
}
}
}; for (int i = 0; i < 10; i++) {
Thread t = new Thread(runnable);
t.start();
}
}
}

运行结果:

总体来说,如果了解到整个实现流程,使用zookeeper实现分布式锁并不是很困难,不过这也只是一个简单的实现,与前面实现Redis实现相比,本实现的稳定性更强,这是因为zookeeper的特性所致,在外界看来,zookeeper集群中每一个节点都是一致的。

完整代码可以在我的GitHub中查看:https://github.com/hongmoshui/DistributedLock

原文链接:https://www.cnblogs.com/liuyang0/p/6800538.html

分布式锁的实现【基于ZooKeeper】的更多相关文章

  1. 分布式锁(3) ----- 基于zookeeper的分布式锁

    分布式锁系列文章 分布式锁(1) ----- 介绍和基于数据库的分布式锁 分布式锁(2) ----- 基于redis的分布式锁 分布式锁(3) ----- 基于zookeeper的分布式锁 代码:ht ...

  2. 分布式锁与实现--基于ZooKeeper实现

    引言 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提 ...

  3. 分布式锁(2) ----- 基于redis的分布式锁

    分布式锁系列文章 分布式锁(1) ----- 介绍和基于数据库的分布式锁 分布式锁(2) ----- 基于redis的分布式锁 分布式锁(3) ----- 基于zookeeper的分布式锁 代码:ht ...

  4. 分布式锁用Redis还是ZooKeeper?(转载)

    文章系网络转载,侵删. 来源:https://zhuanlan.zhihu.com/p/73807097 为什么用分布式锁?在讨论这个问题之前,我们先来看一个业务场景. 图片来自 Pexels 为什么 ...

  5. 分布式锁用Redis与Zookeeper的使用

    为什么用分布式锁?   在讨论这个问题之前,我们先来看一个业务场景: 系统A是一个电商系统,目前是一台机器部署,系统中有一个用户下订单的接口,但是用户下订单之前一定要去检查一下库存,确保库存足够了才会 ...

  6. 分布式锁(一) Zookeeper分布式锁

    什么是Zookeeper? Zookeeper(业界简称zk)是一种提供配置管理.分布式协同以及命名的中心化服务,这些提供的功能都是分布式系统中非常底层且必不可少的基本功能,但是如果自己实现这些功能而 ...

  7. 服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁

    一.基于key/value实现 我们在构建分布式系统的时候,经常需要控制对共享资源的互斥访问.这个时候我们就涉及到分布式锁(也称为全局锁)的实现,基于目前的各种工具,我们已经有了大量的实现方式,比如: ...

  8. 分布式锁中的基于redis的setnx的原理以及set和setnx的区别是什么

    基于Redis实现分布式锁.虽然网上介绍的Redis分布式锁博客比较多,却有着各种各样的问题,本篇博客将详细介绍如何正确地使用setnx实现Redis分布式锁 这里就不介绍错误的示范了 大家直接看正确 ...

  9. 分布式锁为什么要选择Zookeeper而不是Redis?

    在分布式的应用中,为了防止单点故障,保障高可用,通常会采用主从结构,当主节点挂掉后,从节点可以代替主节点提供服务. Redis通过复制 + sentinel哨兵来实现主从模式. Zookeeper通过 ...

  10. 分布式锁之二:zookeeper分布式锁2

    示例: package com.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zoo ...

随机推荐

  1. 快速构建Spring Cloud工程

    spring cloud简介 spring cloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.微代理.事件总线.全局锁.决策竞选.分布式会话等等.它运行环境 ...

  2. jvm监测

    jvm调优,首先,你得会jvm性能检测.开方得先诊断啊.

  3. P4999烦(gui)人(chu)的数学作业

    P4399P4999 这是一道有着三倍经验的宝藏题目 我们可以求出来1到n中,1~9分别出现了几次,设f[i]为数字i出现的次数,则\(ans=\sum{f[i]\cdot i}\) 然后就是数位dp ...

  4. PHP文件和目录操作

    目录操作 创建目录:mkdir(目录地址, 权限, 是否递归创建=false); 删除目录:rmdir(目录地址);(仅仅可以删除空目录,不支持递归删除) 移动(改名):rename(旧地址, 新地址 ...

  5. oracle系统调优

    在Oracle数据库系统中,起到调节作用的参数叫初始化参数,在Oracle 8i及以前的版本中,这些初始化参数记录在INITsid.ora文件中:而Oracle 9i/10g/11g中将这些参数记录在 ...

  6. Python 笔试集:什么时候 i = i + 1 并不等于 i += 1?

    ​​增强型赋值语句是经常被使用到的,因为从各种学习渠道中,我们能够得知 i += 1 的效率往往要比 i = i + 1 更高一些(这里以 += 为例,实际上增强型赋值语句不仅限于此).所以我们会乐此 ...

  7. 发邮件--yagmail模块

    准备工作:1.在你的邮箱设置里面打开smtp服务(若有的话)2.开启邮箱授权码,记住这个授权码(连接邮箱服务时用) 1.安装yagmail模块pip install yagmail2.举例:impor ...

  8. 接口开发01--mock接口

    开发接口的常见场景: 1.mock接口,模拟一些接口,在别的接口没有开发好的时候,你需要测试,可以先模拟一个假接口来测试.比如常见 2.若需要调用第三方接口时,比如支付接口. 3.查看数据,比如开放数 ...

  9. 红米note2 刷机 注意问题:

    其他的百度都有,用刷线宝刷 红米note2 刷机   注意问题: 关机状态线下,链接电脑,按着音量下键不松手,按电源键开机后松开,即进入刷机模式. 其中,红米,红米1s移动,红米note移动3g/联通 ...

  10. Delphi控件-复合控件

     http://blog.csdn.net/cml2030/article/details/3166634 Delphi控件-复合控件 标签: delphidestructorbuttonstring ...