利用Zookeeper实现分布式锁及服务注册中心

Posted lonelyxmas

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用Zookeeper实现分布式锁及服务注册中心相关的知识,希望对你有一定的参考价值。

原文:利用Zookeeper实现分布式锁及服务注册中心

对于Zookeeper的定义以及原理,网上已经有很多的优秀文章对其进行了详细的介绍,所以本文不再进行这方面的阐述。

本文主要介绍一些基本的准备工作以及zookeeper.net的使用。

本文源代码github地址:https://github.com/Mike-Zrw/ZookeeperHelper

zookeeper下载地址:https://archive.apache.org/dist/zookeeper/

ZooInspector下载地址:https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip

Zookeeper下载及安装

目前版本最新的为3.5.3 。 本文使用的版本为3.4.10

随意下载一个版本的压缩包,解压到某个文件夹中. zookeeper服务的启动脚本在bin目录下的文件:zkServer.cmd

在服务启动之前,需要对配置文件进行基本的设置:

conf目录下的zoo_sample.cfg改名为 zoo.cf

修改里面的日志文件路径,我修改完之后的文件内容如下

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=data
dataLogDir=datalog
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1

  • tickTime:Zookeeper之间维持心跳的时间间隔
  • initLimit:集群中的follower服务器(F)与leader服务器(L)之间 初始连接 时能容忍的最多心跳数(tickTime的数量),这里为2000*10 ,超过10次心跳仍然没同步完成,会重新选举leader
  • syncLimit:集群中的follower服务器(F)与leader服务器(L)之间 请求和应答 之间能容忍的最多心跳数(tickTime的数量)。超时follwer将被丢弃
  • dataDir:Zookeeper保存数据的目录 ,目录不可含中文字符
  • dataLogDir:Zookeeper保存日志文件的目录
  • clientPort:Zookeeper服务器的端口

配置完成之后就可以双击zkServer.cmd启动zookeeper服务了
技术分享图片

ZooInspector的安装

ZooInspector是zookeeper的监视工具,可以查看zookeeper的数据信息

下载完成后直接解压,运行zookeeper-dev-ZooInspector.jar。如果默认端口没有修改,直接点击连接就可以了

技术分享图片

ZooKeeper.Net

通过Nuget来安装ZooKeeper.Net的开发包到项目中
技术分享图片

下面的代码会建立一个zookeeper的连接,并创建一个名为parent的临时目录

   public static void TestConnect()
    {
        Console.WriteLine("建立连接");
        //服务地址为:localhost:2181  超时连接30秒
        using (ZooKeeper Instance = new ZooKeeperNet.ZooKeeper("localhost:2181", new TimeSpan(0, 0, 30), new Watcher("new")))
        {
            Console.WriteLine("检测是否有parent目录");
            var sdata = Instance.Exists("/parent", new Watcher("exists"));
            Console.WriteLine(sdata==null?"否":"是");
            if (sdata==null)
            {
                Console.WriteLine("开始创建parent目录");
                //data:目录关联的data为:this is the parentnode data
                //acl:目录的访问权限控制
                //CreateMode:Ephemeral:目录为临时目录,断开连接目录会自动清除  永久目录:Persistent  自增目录:***Sequential
                Instance.Create("/parent", "this is the parentnode data".GetBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.Ephemeral);
                Console.WriteLine("创建parent目录完成");
                Console.WriteLine("检测是否有parent目录");
                sdata = Instance.Exists("/parent", new Watcher("exists2"));
                Console.WriteLine(sdata == null ? "否" : "是");
                if (sdata != null)
                {
                    Console.WriteLine("删除parent目录");
                    Instance.Delete("/parent", 0);
                }
            }
        }
    }

运行结果如下

技术分享图片

需要注意的是执行这段代码可能会报错提示connection loss,出现错误的原因是建立连接的这一步是异步的操作,应当等待zookeeper连接成功之后再执行下面的代码。所以这段代码只是进行一个简单的演示,nuget上附有正确的使用方式

关于zookeeper的watch定义如下:

监视是一种简单的机制,使客户端收到关于ZooKeeper集合中的更改的通知。客户端可以在读取特定znode时设置Watches。Watches会向注册的客户端发送任何znode(客户端注册表)更改的通知。

Znode更改是与znode相关的数据的修改或znode的子项中的更改。只触发一次watches。如果客户端想要再次通知,则必须通过另一个读取操作来完成。当连接会话过期时,客户端将与服务器断开连接,相关的watches也将被删除。

Zookeeper的每个节点称之为znode,znode的类型分为四种:持久节点,临时节点,持久顺序节点,临时顺序节点

这里简单说下顺序节点:比如我需要建立一个名称为 /app的节点,那么zookeeper会建立一个名为/app0000000001的节点,如果再有人要创建一个名为/app的顺序节点,新建的节点名称会变为/app0000000002

也就是说顺序节点生成之后的名字是指定的名字加十位序列号,序列号不会重复,即使是两个操作同时创建。 我们可以利用这一个特性来实现分布式锁:

分布式锁的伪代码如下:

public void lock(){
var parentpath=/lock_
var path = CreateEphemeralSequentialNode(parentpath)
while(true){
var children = 获取父节点的所有子节点
if(path是children中的最小的){
成功获取锁
return;
}else{
监控等待前一个节点删除
wait();
}
}
}

具体的代码可以在github上查看












































以上是关于利用Zookeeper实现分布式锁及服务注册中心的主要内容,如果未能解决你的问题,请参考以下文章

注册中心zookeeper-3.4.6集群以及高可用

Dubbo注册中心介绍

使用Spring Cloud搭建服务注册中心

dubbo服务治理中间件,zookeeper注册中心

分布式服务----Zookeeper注册中心

Zookeeper