分布式锁的实现【基于ZooKeeper】
引言
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】的更多相关文章
- 分布式锁(3) ----- 基于zookeeper的分布式锁
分布式锁系列文章 分布式锁(1) ----- 介绍和基于数据库的分布式锁 分布式锁(2) ----- 基于redis的分布式锁 分布式锁(3) ----- 基于zookeeper的分布式锁 代码:ht ...
- 分布式锁与实现--基于ZooKeeper实现
引言 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提 ...
- 分布式锁(2) ----- 基于redis的分布式锁
分布式锁系列文章 分布式锁(1) ----- 介绍和基于数据库的分布式锁 分布式锁(2) ----- 基于redis的分布式锁 分布式锁(3) ----- 基于zookeeper的分布式锁 代码:ht ...
- 分布式锁用Redis还是ZooKeeper?(转载)
文章系网络转载,侵删. 来源:https://zhuanlan.zhihu.com/p/73807097 为什么用分布式锁?在讨论这个问题之前,我们先来看一个业务场景. 图片来自 Pexels 为什么 ...
- 分布式锁用Redis与Zookeeper的使用
为什么用分布式锁? 在讨论这个问题之前,我们先来看一个业务场景: 系统A是一个电商系统,目前是一台机器部署,系统中有一个用户下订单的接口,但是用户下订单之前一定要去检查一下库存,确保库存足够了才会 ...
- 分布式锁(一) Zookeeper分布式锁
什么是Zookeeper? Zookeeper(业界简称zk)是一种提供配置管理.分布式协同以及命名的中心化服务,这些提供的功能都是分布式系统中非常底层且必不可少的基本功能,但是如果自己实现这些功能而 ...
- 服务注册发现consul之四: 分布式锁之四:基于Consul的KV存储和分布式信号量实现分布式锁
一.基于key/value实现 我们在构建分布式系统的时候,经常需要控制对共享资源的互斥访问.这个时候我们就涉及到分布式锁(也称为全局锁)的实现,基于目前的各种工具,我们已经有了大量的实现方式,比如: ...
- 分布式锁中的基于redis的setnx的原理以及set和setnx的区别是什么
基于Redis实现分布式锁.虽然网上介绍的Redis分布式锁博客比较多,却有着各种各样的问题,本篇博客将详细介绍如何正确地使用setnx实现Redis分布式锁 这里就不介绍错误的示范了 大家直接看正确 ...
- 分布式锁为什么要选择Zookeeper而不是Redis?
在分布式的应用中,为了防止单点故障,保障高可用,通常会采用主从结构,当主节点挂掉后,从节点可以代替主节点提供服务. Redis通过复制 + sentinel哨兵来实现主从模式. Zookeeper通过 ...
- 分布式锁之二:zookeeper分布式锁2
示例: package com.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zoo ...
随机推荐
- Amaze ui tree 刷新数据
最近在做代码生成器,界面用的是Amaze ui ,但是在用tree的时候发现数据绑定不会自动更新,去百度.谷歌查也没有查到,没办法只能自己做一个demo 在这贴上效果图 demo 下载地址https: ...
- Xcode工程文件pbxproj
Xcode工程文件pbxproj Xcode会去读Project.pbxproj文件,把pbxproj转成plist文件,看起根目录结构 rootObject:指向的是我们的工程对象.(对应一个24个 ...
- 架构-数据库访问-SQL语言进行连接数据库服务器-OLE:OLE
ylbtech-架构-数据库访问-SQL语言进行连接数据库服务器-OLE:OLE Object Linking and Embedding,对象连接与嵌入,简称OLE技术.OLE 不仅是桌面应用程序集 ...
- 协议-TCP:TCP
ylbtech-协议-TCP:TCP 传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的.可靠的.基于字节流的传输层通信协议,由IETF的RFC 793 ...
- jmeter测试jdbc、mysql
1.打入jar包,在测试计划或者直接放在lib/ext下(第三方的包) 2.添加jdbc-connection-config,variable name 和后面的jdbc请求的参数保持一致 Datab ...
- mkdir: 无法创建目录"kk": 只读文件系统
创建文件提示 root@hap1:/test>mkdir kk mkdir: 无法创建目录"kk": 只读文件系统 root@hap1:/test>mount .... ...
- lateral view 使用方法
这个函数相当于拆开行变成列. 可以理解为行转列. select id,order_label from table_bx lateral view explode(split(work_order_l ...
- 从Android手机中取出已安装的app包,导出apk
从Android手机中取出已安装的app包,导出apk TAG:Android,提取,apk,adb,pm,root,导出apk 假设有这样一个场景,A君看到你手机上一个实用APP,想要安装到自己手机 ...
- Ngix 配置与部署(wsgi,uwsgi,uWSGI)
1. WSGI 是一种协议接口,他是描述web服务器如何与web应用程序(Django ,Flask ) 通讯的规范. 2. uwsgi 与WSGI协议一样,是uWSGI服务器的独占协议,用于定义传输 ...
- 浅谈Vue中的$set的使用
在我们使用vue进行开发的过程中,可能会遇到一种情况:当生成vue实例后,当再次给数据赋值时,有时候并不会自动更新到视图上去: 当我们去看vue文档的时候,会发现有这么一句话:如果在实例创建之后添加新 ...