引言

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来监听前一个节点。

分布式锁

  1. import org.apache.zookeeper.*;
  2. import org.apache.zookeeper.data.Stat;
  3.  
  4. import java.io.IOException;
  5. import java.util.ArrayList;
  6. import java.util.Collections;
  7. import java.util.List;
  8. import java.util.concurrent.CountDownLatch;
  9. import java.util.concurrent.TimeUnit;
  10. import java.util.concurrent.locks.Condition;
  11. import java.util.concurrent.locks.Lock;
  12.  
  13. /**
  14. * Created by liuyang on 2017/4/20.
  15. */
  16. public class DistributedLock implements Lock, Watcher {
  17. private ZooKeeper zk = null;
  18. // 根节点
  19. private String ROOT_LOCK = "/locks";
  20. // 竞争的资源
  21. private String lockName;
  22. // 等待的前一个锁
  23. private String WAIT_LOCK;
  24. // 当前锁
  25. private String CURRENT_LOCK;
  26. // 计数器
  27. private CountDownLatch countDownLatch;
  28. private int sessionTimeout = 30000;
  29. private List<Exception> exceptionList = new ArrayList<Exception>();
  30.  
  31. /**
  32. * 配置分布式锁
  33. * @param config 连接的url
  34. * @param lockName 竞争资源
  35. */
  36. public DistributedLock(String config, String lockName) {
  37. this.lockName = lockName;
  38. try {
  39. // 连接zookeeper
  40. zk = new ZooKeeper(config, sessionTimeout, this);
  41. Stat stat = zk.exists(ROOT_LOCK, false);
  42. if (stat == null) {
  43. // 如果根节点不存在,则创建根节点
  44. zk.create(ROOT_LOCK, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
  45. }
  46. } catch (IOException e) {
  47. e.printStackTrace();
  48. } catch (InterruptedException e) {
  49. e.printStackTrace();
  50. } catch (KeeperException e) {
  51. e.printStackTrace();
  52. }
  53. }
  54.  
  55. // 节点监视器
  56. public void process(WatchedEvent event) {
  57. if (this.countDownLatch != null) {
  58. this.countDownLatch.countDown();
  59. }
  60. }
  61.  
  62. public void lock() {
  63. if (exceptionList.size() > 0) {
  64. throw new LockException(exceptionList.get(0));
  65. }
  66. try {
  67. if (this.tryLock()) {
  68. System.out.println(Thread.currentThread().getName() + " " + lockName + "获得了锁");
  69. return;
  70. } else {
  71. // 等待锁
  72. waitForLock(WAIT_LOCK, sessionTimeout);
  73. }
  74. } catch (InterruptedException e) {
  75. e.printStackTrace();
  76. } catch (KeeperException e) {
  77. e.printStackTrace();
  78. }
  79. }
  80.  
  81. public boolean tryLock() {
  82. try {
  83. String splitStr = "_lock_";
  84. if (lockName.contains(splitStr)) {
  85. throw new LockException("锁名有误");
  86. }
  87. // 创建临时有序节点
  88. CURRENT_LOCK = zk.create(ROOT_LOCK + "/" + lockName + splitStr, new byte[0],
  89. ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
  90. System.out.println(CURRENT_LOCK + " 已经创建");
  91. // 取所有子节点
  92. List<String> subNodes = zk.getChildren(ROOT_LOCK, false);
  93. // 取出所有lockName的锁
  94. List<String> lockObjects = new ArrayList<String>();
  95. for (String node : subNodes) {
  96. String _node = node.split(splitStr)[0];
  97. if (_node.equals(lockName)) {
  98. lockObjects.add(node);
  99. }
  100. }
  101. Collections.sort(lockObjects);
  102. System.out.println(Thread.currentThread().getName() + " 的锁是 " + CURRENT_LOCK);
  103. // 若当前节点为最小节点,则获取锁成功
  104. if (CURRENT_LOCK.equals(ROOT_LOCK + "/" + lockObjects.get(0))) {
  105. return true;
  106. }
  107.  
  108. // 若不是最小节点,则找到自己的前一个节点
  109. String prevNode = CURRENT_LOCK.substring(CURRENT_LOCK.lastIndexOf("/") + 1);
  110. WAIT_LOCK = lockObjects.get(Collections.binarySearch(lockObjects, prevNode) - 1);
  111. } catch (InterruptedException e) {
  112. e.printStackTrace();
  113. } catch (KeeperException e) {
  114. e.printStackTrace();
  115. }
  116. return false;
  117. }
  118.  
  119. public boolean tryLock(long timeout, TimeUnit unit) {
  120. try {
  121. if (this.tryLock()) {
  122. return true;
  123. }
  124. return waitForLock(WAIT_LOCK, timeout);
  125. } catch (Exception e) {
  126. e.printStackTrace();
  127. }
  128. return false;
  129. }
  130.  
  131. // 等待锁
  132. private boolean waitForLock(String prev, long waitTime) throws KeeperException, InterruptedException {
  133. Stat stat = zk.exists(ROOT_LOCK + "/" + prev, true);
  134.  
  135. if (stat != null) {
  136. System.out.println(Thread.currentThread().getName() + "等待锁 " + ROOT_LOCK + "/" + prev);
  137. this.countDownLatch = new CountDownLatch(1);
  138. // 计数等待,若等到前一个节点消失,则precess中进行countDown,停止等待,获取锁
  139. this.countDownLatch.await(waitTime, TimeUnit.MILLISECONDS);
  140. this.countDownLatch = null;
  141. System.out.println(Thread.currentThread().getName() + " 等到了锁");
  142. }
  143. return true;
  144. }
  145.  
  146. public void unlock() {
  147. try {
  148. System.out.println("释放锁 " + CURRENT_LOCK);
  149. zk.delete(CURRENT_LOCK, -1);
  150. CURRENT_LOCK = null;
  151. zk.close();
  152. } catch (InterruptedException e) {
  153. e.printStackTrace();
  154. } catch (KeeperException e) {
  155. e.printStackTrace();
  156. }
  157. }
  158.  
  159. public Condition newCondition() {
  160. return null;
  161. }
  162.  
  163. public void lockInterruptibly() throws InterruptedException {
  164. this.lock();
  165. }
  166.  
  167. public class LockException extends RuntimeException {
  168. private static final long serialVersionUID = 1L;
  169. public LockException(String e){
  170. super(e);
  171. }
  172. public LockException(Exception e){
  173. super(e);
  174. }
  175. }
  176. }
  1. 测试代码
  1. public class Test {
  2. static int n = 500;
  3.  
  4. public static void secskill() {
  5. System.out.println(--n);
  6. }
  7.  
  8. public static void main(String[] args) {
  9.  
  10. Runnable runnable = new Runnable() {
  11. public void run() {
  12. DistributedLock lock = null;
  13. try {
  14. lock = new DistributedLock("127.0.0.1:2181", "test1");
  15. lock.lock();
  16. secskill();
  17. System.out.println(Thread.currentThread().getName() + "正在运行");
  18. } finally {
  19. if (lock != null) {
  20. lock.unlock();
  21. }
  22. }
  23. }
  24. };
  25.  
  26. for (int i = 0; i < 10; i++) {
  27. Thread t = new Thread(runnable);
  28. t.start();
  29. }
  30. }
  31. }

运行结果:

总体来说,如果了解到整个实现流程,使用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. Amaze ui tree 刷新数据

    最近在做代码生成器,界面用的是Amaze ui ,但是在用tree的时候发现数据绑定不会自动更新,去百度.谷歌查也没有查到,没办法只能自己做一个demo 在这贴上效果图 demo 下载地址https: ...

  2. Xcode工程文件pbxproj

    Xcode工程文件pbxproj Xcode会去读Project.pbxproj文件,把pbxproj转成plist文件,看起根目录结构 rootObject:指向的是我们的工程对象.(对应一个24个 ...

  3. 架构-数据库访问-SQL语言进行连接数据库服务器-OLE:OLE

    ylbtech-架构-数据库访问-SQL语言进行连接数据库服务器-OLE:OLE Object Linking and Embedding,对象连接与嵌入,简称OLE技术.OLE 不仅是桌面应用程序集 ...

  4. 协议-TCP:TCP

    ylbtech-协议-TCP:TCP 传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的.可靠的.基于字节流的传输层通信协议,由IETF的RFC 793 ...

  5. jmeter测试jdbc、mysql

    1.打入jar包,在测试计划或者直接放在lib/ext下(第三方的包) 2.添加jdbc-connection-config,variable name 和后面的jdbc请求的参数保持一致 Datab ...

  6. mkdir: 无法创建目录"kk": 只读文件系统

    创建文件提示 root@hap1:/test>mkdir kk mkdir: 无法创建目录"kk": 只读文件系统 root@hap1:/test>mount .... ...

  7. lateral view 使用方法

    这个函数相当于拆开行变成列. 可以理解为行转列. select id,order_label from table_bx lateral view explode(split(work_order_l ...

  8. 从Android手机中取出已安装的app包,导出apk

    从Android手机中取出已安装的app包,导出apk TAG:Android,提取,apk,adb,pm,root,导出apk 假设有这样一个场景,A君看到你手机上一个实用APP,想要安装到自己手机 ...

  9. Ngix 配置与部署(wsgi,uwsgi,uWSGI)

    1. WSGI 是一种协议接口,他是描述web服务器如何与web应用程序(Django ,Flask ) 通讯的规范. 2. uwsgi 与WSGI协议一样,是uWSGI服务器的独占协议,用于定义传输 ...

  10. 浅谈Vue中的$set的使用

    在我们使用vue进行开发的过程中,可能会遇到一种情况:当生成vue实例后,当再次给数据赋值时,有时候并不会自动更新到视图上去: 当我们去看vue文档的时候,会发现有这么一句话:如果在实例创建之后添加新 ...