zookeeper怎么实现分布式锁

作者&投稿:蓍弦 (若有异议请与网页底部的电邮联系)
zookeeper集群,分布式锁怎么保证正确~

1. 利用节点名称的唯一性来实现共享锁
ZooKeeper抽象出来的节点结构是一个和unix文件系统类似的小型的树状的目录结构。ZooKeeper机制规定:同一个目录下只能有一个唯一的文件名。例如:我们在Zookeeper目录/test目录下创建,两个客户端创建一个名为Lock节点,只有一个能够成功。
算法思路: 利用名称唯一性,加锁操作时,只需要所有客户端一起创建/test/Lock节点,只有一个创建成功,成功者获得锁。解锁时,只需删除/test/Lock节点,其余客户端再次进入竞争创建节点,直到所有客户端都获得锁。
基于以上机制,利用节点名称唯一性机制的共享锁算法流程如图所示:

该共享锁实现很符合我们通常多个线程去竞争锁的概念,利用节点名称唯一性的做法简明、可靠。
由上述算法容易看出,由于客户端会同时收到/test/Lock被删除的通知,重新进入竞争创建节点,故存在"惊群现象"。
使用该方法进行测试锁的性能列表如下:

总结 这种方案的正确性和可靠性是ZooKeeper机制保证的,实现简单。缺点是会产生“惊群”效应,假如许多客户端在等待一把锁,当锁释放时候所有客户端都被唤醒,仅仅有一个客户端得到锁。

2. 利用临时顺序节点实现共享锁的一般做法
首先介绍一下,Zookeeper中有一种节点叫做顺序节点,故名思议,假如我们在/lock/目录下创建节3个点,ZooKeeper集群会按照提起创建的顺序来创建节点,节点分别为/lock/0000000001、/lock/0000000002、/lock/0000000003。
ZooKeeper中还有一种名为临时节点的节点,临时节点由某个客户端创建,当客户端与ZooKeeper集群断开连接,则开节点自动被删除。
利用上面这两个特性,我们来看下获取实现分布式锁的基本逻辑:
客户端调用create()方法创建名为“locknode/guid-lock-”的节点,需要注意的是,这里节点的创建类型需要设置为EPHEMERAL_SEQUENTIAL。
客户端调用getChildren(“locknode”)方法来获取所有已经创建的子节点,同时在这个节点上注册上子节点变更通知的Watcher。
客户端获取到所有子节点path之后,如果发现自己在步骤1中创建的节点是所有节点中序号最小的,那么就认为这个客户端获得了锁。
如果在步骤3中发现自己并非是所有子节点中最小的,说明自己还没有获取到锁,就开始等待,直到下次子节点变更通知的时候,再进行子节点的获取,判断是否获取锁。
释放锁的过程相对比较简单,就是删除自己创建的那个子节点即可。
上面这个分布式锁的实现中,大体能够满足了一般的分布式集群竞争锁的需求。这里说的一般性场景是指集群规模不大,一般在10台机器以内。
不过,细想上面的实现逻辑,我们很容易会发现一个问题,步骤4,“即获取所有的子点,判断自己创建的节点是否已经是序号最小的节点”,这个过程,在整个分布式锁的竞争过程中,大量重复运行,并且绝大多数的运行结果都是判断出自己并非是序号最小的节点,从而继续等待下一次通知——这个显然看起来不怎么科学。客户端无端的接受到过多的和自己不相关的事件通知,这如果在集群规模大的时候,会对Server造成很大的性能影响,并且如果一旦同一时间有多个节点的客户端断开连接,这个时候,服务器就会像其余客户端发送大量的事件通知——这就是所谓的惊群效应。而这个问题的根源在于,没有找准客户端真正的关注点。

  1. 不能重入

  2. 没有本地锁,并发性能会比较差,不使用用在并发争锁较多的场景下。本地锁非自旋
  3. 未考虑锁等待排序. 这个是redis很难实现的.

  可以通过redis的list实现,但缺点是list下每个子节点无超时时间. redis也无法进行模糊查询 key*.
  故还是通过zookeeper实现比较好. 但zookeeper 会遇到性能瓶颈,我们线下的就出现了,经常注册不上的情况.
  zookeeper原理是临时节点
  

这种实现非常简单,具体的流程如下


 对应的实现如下

Java代码  

package zk.lock;  

  

  

import zk.util.NetworkUtil;  

import zk.util.ZKUtil;  

  

/** 

 * User: zhenghui 

 * Date: 14-3-26 

 * Time: 下午8:37 

 * 分布式锁实现. 

 * 

 * 这种实现的原理是,创建某一个任务的节点,比如 /lock/tasckname 然后获取对应的值,如果是当前的Ip,那么获得锁,如果不是,则没获得 

 * .如果该节点不存在,则创建该节点,并把改节点的值设置成当前的IP 

 */  

public class DistributedLock01 {  

  

    private ZKClient zkClient;  

  

  

    public static final String LOCK_ROOT = "/lock";  

    private String lockName;  

  

  

    public DistributedLock01(String connectString, int sessionTimeout,String lockName) throws Exception {  

        //先创建zk链接.  

        this.createConnection(connectString,sessionTimeout);  

  

        this.lockName = lockName;  

    }  

  

    public boolean tryLock(){  

        String path = ZKUtil.contact(LOCK_ROOT,lockName);  

        String localIp = NetworkUtil.getNetworkAddress();  

        try {  

            if(zkClient.exists(path)){  

                String ownnerIp = zkClient.getData(path);  

                if(localIp.equals(ownnerIp)){  

                    return true;  

                }  

            } else {  

                zkClient.createPathIfAbsent(path,false);  

                if(zkClient.exists(path)){  

                    String ownnerIp = zkClient.getData(path);  

                    if(localIp.equals(ownnerIp)){  

                        return true;  

                    }  

                }  

            }  

        } catch (Exception e) {  

            e.printStackTrace();  

        }  

        return false;  

    }  

  

  

    /** 

     * 创建zk连接 

     * 

     */  

    protected void createConnection(String connectString, int sessionTimeout) throws Exception {  

        if(zkClient != null){  

            releaseConnection();  

        }  

        zkClient = new ZKClient(connectString,sessionTimeout);  

        zkClient.createPathIfAbsent(LOCK_ROOT,true);  

    }  

    /** 

     * 关闭ZK连接 

     */  

    protected void releaseConnection() throws InterruptedException {  

        if (zkClient != null) {  

            zkClient.close();  

        }  

    }  

  

}  



很多使用Zookeeper的情景是需要我们嵌入Zookeeper作为自己的分布式应用系统的一部分来提供分布式服务,此时我们需要通过程序的方式来启动Zookeeper。此时可以通过Zookeeper API的ZooKeeperServerMain类来启动Zookeeper服务。


zookeeper集群部署和单机部署的区别和优缺点
ookeeper是什么Zookeeper,一种分布式应用的协作服务,是Google的Chubby一个开源的实现,是Hadoop的分布式协调服务,它包含一个简单的原语集,应用于分布式应用的协作服务,使得分布式应用可以基于这些接口实现诸如同步、配置维护和分集群或者命名的服务。zookeeper是一个由多个service组成的集群,一个leader,多个follower,每个server保...

如何实现高可用的 redis 集群
并且根据集群的 group 数,进行一致性哈希计算,确定 key 唯一落入的 group ,随后对这个 group 的主库进行操作。客户端会在Z ooKeeper 设置监视,当某个 group 的主库发生变化时,Z ooKeeper 会主动通知客户端,客户端会更新对应 group 的最新主库。我们的Redis 集群是以业务为单位进行划分的,不...

灵寿县15635918505: zookeeper怎么实现分布式锁 -
富琬艾迪: 很多使用Zookeeper的情景是需要我们嵌入Zookeeper作为自己的分布式应用系统的一部分来提供分布式服务,此时我们需要通过程序的方式来启动Zookeeper.此时可以通过Zookeeper API的ZooKeeperServerMain类来启动Zookeeper服务.

灵寿县15635918505: 如何使用redis和zookeeper实现分布式锁 -
富琬艾迪: 1. 利用节点名称的唯一性来实现共享锁 ZooKeeper抽象出来的节点结构是一个和unix文件系统类似的小型的树状的目录结构.ZooKeeper机制规定:同一个目录下只能有一个唯一的文件名.例如:我们在Zookeeper目录/test目录下创建,两个客...

灵寿县15635918505: java分布式更新数据库如何加锁
富琬艾迪: 使用分布式锁 可以使用zookeeper实现

灵寿县15635918505: 如何用redis实现分布式锁 -
富琬艾迪: Redis有一系列的命令,特点是以NX结尾,NX是Not eXists的缩写,如SETNX命令就应该理解为:SET if Not eXists.这系列的命令非常有用,这里讲使用SETNX来实现分布式锁.用SETNX实现分布式锁 利用SETNX非常简单地实现分布式锁....

灵寿县15635918505: zookeeper 节点有什么用 -
富琬艾迪: ZooKeeper 节点是有生命周期的,这取决于节点的类型.在 ZooKeeper 中,节点类型可以分为持久节点(PERSISTENT )、临时节点(EPHEMERAL),以及时序节点(SEQUENTIAL ),具体在节点创建过程中,一般是组合使用,可以生成...

灵寿县15635918505: 分布式锁用zookeeper还是redis好 -
富琬艾迪: 1. 不能重入2. 没有本地锁,并发性能会比较差,不使用用在并发争锁较多的场景下.本地锁非自旋 3. 未考虑锁等待排序. 这个是redis很难实现的.可以通过redis的list实现,但缺点是list下每个子节点无超时时间. redis也无法进行模糊查询 key*. 故还是通过zookeeper实现比较好. 但zookeeper 会遇到性能瓶颈,我们线下的就出现了,经常注册不上的情况. zookeeper原理是临时节点

灵寿县15635918505: 如何检测一台机器是否宕机 -
富琬艾迪: 检测一台机器是否宕机的应用场景如下:1, 工作机器宕机,总控节点需要能够检测到并且将原有服务迁移到集群中的其它节点.2, 总控节点宕机,总控节点的备份节点(一般称为Slave)需要能够检测到并替换成主节点继续对外服务.检...

灵寿县15635918505: zookeeper解决了哪些问题 -
富琬艾迪: 一旦workerA挂了,很有可能会影响集群运行状态,Backup收不到响应或者收到响应延迟的原因是因为网络阻塞的问题 呢,接替Master的工作自己成为Master,传统的配置文件分发都是需要把配置文件数据分发到每台worker上,然后进行worker...

灵寿县15635918505: ZooKeeper可以作为分布式存储系统么 -
富琬艾迪: ZooKeeper是一个应用服务器(也可称之为事务管理服务器),并不是为存储数据而设计的,不适合作存储系统. ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重...

灵寿县15635918505: java分布式锁的实现方式有哪些 -
富琬艾迪: 之前在itjob技术群讨论过这个1、数据库级别控制,乐观锁控制2、类似zookeeper做一个远程单点锁,每次取锁、加锁、释放锁 还有没有更优解,上面两种哪个好点 简单说就是http请求,100ms内两个同样的请求,{查询接口拿一个key和一个数值,然后+1,请求新数值},加{}的这个过程希望相同key的请求能够串行,否则设置新值会有并发问题

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网