Zookeeper相关知识

一.Zookeeper是什么?html

Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中常常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。java

Zookeeper 做为 Hadoop 项目中的一个子项目,是 Hadoop 集群管理的一个必不可少的模块,它主要用来控制集群中的数据,如它管理 Hadoop 集群中的 NameNode,还有 Hbase 中 Master Election、Server 之间状态同步等。node

二.Zookeeper能作什么?shell

Zookeeper 做为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于相似于文件系统的目录节点树方式的数据存储,可是 Zookeeper 并非用来专门存储数据的,它的做用主要是用来维护和监控你存储的数据的状态变化。经过监控这些数据状态的变化,从而能够达到基于数据的集群管理。数据库

ZooKeeper 主要是用来维护和监控一个目录节点树中存储的数据的状态,全部咱们可以操做 ZooKeeper 的也和操做目录节点树大致同样,如建立一个目录节点,给某个目录节点设置数据,获取某个目录节点的全部子目录节点,给某个目录节点设置权限和监控这个目录节点的状态变化。apache

Zookeeper 从设计模式角度来看,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理你们都关心的数据,而后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper 就将负责通知已经在 Zookeeper 上注册的那些观察者作出相应的反应,从而实现集群中相似 Master/Slave 管理模式,关于 Zookeeper 的详细架构等内部细节能够阅读 Zookeeper 的源码。windows

三.Zookeeper环境如何搭建?设计模式

安装和配置详解

本文介绍的 Zookeeper 是以 3.2.2 这个稳定版本为基础,最新的版本能够经过官网http://hadoop.apache.org/zookeeper/来获取,Zookeeper 的安装很是简单,下面将从单机模式和集群模式两个方面介绍 Zookeeper 的安装和配置。api

单机模式

单机安装很是简单,只要获取到 Zookeeper 的压缩包并解压到某个目录如:/home/zookeeper-3.2.2 下,Zookeeper 的启动脚本在 bin 目录下,Linux 下的启动脚本是 zkServer.sh,在 3.2.2 这个版本 Zookeeper 没有提供 windows 下的启动脚本,因此要想在 windows 下启动 Zookeeper 要本身手工写一个,如清单 1 所示:服务器

清单 1. Windows 下 Zookeeper 启动脚本
 setlocal 
 set ZOOCFGDIR=%~dp0%..\conf 
 set ZOO_LOG_DIR=%~dp0%.. 
 set ZOO_LOG4J_PROP=INFO,CONSOLE 
 set CLASSPATH=%ZOOCFGDIR% 

 set CLASSPATH=%~dp0..\*;%~dp0..\lib\*;%CLASSPATH% 
 set CLASSPATH=%~dp0..\build\classes;%~dp0..\build\lib\*;%CLASSPATH% 
 set ZOOCFG=%ZOOCFGDIR%\zoo.cfg 
 set ZOOMAIN=org.apache.zookeeper.server.ZooKeeperServerMain 
 java "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" 
 -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %* 
 endlocal

 

在你执行启动脚本以前,还有几个基本的配置项须要配置一下,Zookeeper 的配置文件在 conf 目录下,这个目录下有 zoo_sample.cfg 和 log4j.properties,你须要作的就是将 zoo_sample.cfg 更名为 zoo.cfg,由于 Zookeeper 在启动时会找这个文件做为默认配置文件。下面详细介绍一下,这个配置文件中各个配置项的意义。

 tickTime=2000 
 dataDir=D:/devtools/zookeeper-3.2.2/build 
 clientPort=2181

 

  • tickTime:这个时间是做为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每一个 tickTime 时间就会发送一个心跳。
  • dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认状况下,Zookeeper 将写数据的日志文件也保存在这个目录里。
  • clientPort:这个端口就是客户端链接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。

当这些配置项配置好后,你如今就能够启动 Zookeeper 了,启动后要检查 Zookeeper 是否已经在服务,能够经过 netstat – ano 命令查看是否有你配置的 clientPort 端口号在监听服务。

集群模式

Zookeeper 不只能够单机提供服务,同时也支持多机组成集群来提供服务。实际上 Zookeeper 还支持另一种伪集群的方式,也就是能够在一台物理机上运行多个 Zookeeper 实例,下面将介绍集群模式的安装和配置。

Zookeeper 的集群模式的安装和配置也不是很复杂,所要作的就是增长几个配置项。集群模式除了上面的三个配置项还要增长下面几个配置项:

 initLimit=5 
 syncLimit=2 
 server.1=192.168.211.1:2888:3888 
 server.2=192.168.211.2:2888:3888
  • initLimit:这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户链接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中链接到 Leader 的 Follower 服务器)初始化链接时最长能忍受多少个心跳时间间隔数。当已经超过 10 个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器尚未收到客户端的返回信息,那么代表这个客户端链接失败。总的时间长度就是 5*2000=10 秒
  • syncLimit:这个配置项标识 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 2*2000=4 秒
  • server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的 ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是万一集群中的 Leader 服务器挂了,须要一个端口来从新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通讯的端口。若是是伪集群的配置方式,因为 B 都是同样,因此不一样的 Zookeeper 实例通讯端口号不能同样,因此要给它们分配不一样的端口号。

除了修改 zoo.cfg 配置文件,集群模式下还要配置一个文件 myid,这个文件在 dataDir 目录下,这个文件里面就有一个数据就是 A 的值,Zookeeper 启动时会读取这个文件,拿到里面的数据与 zoo.cfg 里面的配置信息比较从而判断究竟是那个 server。

四.Zookeeper维护及其余操做经常使用命令

4.1 Zookeeper Server经常使用命令

启动Zookeeper服务:   bin/zkServer.sh start
查看Zookeeper服务状态: bin/zkServer.sh status
中止Zookeeper服务:   bin/zkServer.sh stop
重启Zookeeper服务:   bin/zkServer.sh restart


链接服务器:  zkCli.sh -server 127.0.0.1:2181
查看根目录 ls /
建立 testnode节点,关联字符串"HAO"    create /zk/testnode "HAO"


查看节点内容   get /zk/testnode
设置节点内容  set /zk/testnode abc
删除节点    delete /zk/testnode

 

4.2 Zookeeper客户端命令
ZooKeeper命令行工具相似于Linux的shell环境,不过功能确定不及shell啦,可是使用它咱们能够简单的对ZooKeeper进行访问,数据建立,数据修改等操做. 使用 zkCli.sh -server 127.0.0.1:2181 链接到 ZooKeeper 服务,链接成功后,系统会输出 ZooKeeper 的相关环境以及配置信息。命令行工具的一些简单操做以下:

1. 显示根目录下、文件: ls / 使用 ls 命令来查看当前 ZooKeeper 中所包含的内容
2. 显示根目录下、文件: ls2 / 查看当前节点数据并能看到更新次数等数据
3. 建立文件,并设置初始内容: create /zk "test" 建立一个新的 znode节点“ zk ”以及与它关联的字符串
4. 获取文件内容: get /zk 确认 znode 是否包含咱们所建立的字符串
5. 修改文件内容: set /zk "zkbak" 对 zk 所关联的字符串进行设置
6. 删除文件: delete /zk 将刚才建立的 znode 删除
7. 退出客户端: quit
8. 帮助命令: help

 

4.3 ZooKeeper 经常使用四字命令

ZooKeeper 支持某些特定的四字命令字母与其的交互。它们大可能是查询命令,用来获取 ZooKeeper 服务的当前状态及相关信息。用户在客户端能够经过 telnet 或 nc 向 ZooKeeper 提交相应的命令

1. 能够经过命令:echo stat|nc 127.0.0.1 2181 来查看哪一个节点被选择做为follower或者leader
2. 使用echo ruok|nc 127.0.0.1 2181 测试是否启动了该Server,若回复imok表示已经启动。
3. echo dump| nc 127.0.0.1 2181 ,列出未经处理的会话和临时节点。
4. echo kill | nc 127.0.0.1 2181 ,关掉server


5. echo conf | nc 127.0.0.1 2181 ,输出相关服务配置的详细信息。
6. echo cons | nc 127.0.0.1 2181 ,列出全部链接到服务器的客户端的彻底的链接 / 会话的详细信息。
7. echo envi |nc 127.0.0.1 2181 ,输出关于服务环境的详细信息(区别于 conf 命令)。
8. echo reqs | nc 127.0.0.1 2181 ,列出未经处理的请求。


9. echo wchs | nc 127.0.0.1 2181 ,列出服务器 watch 的详细信息。
10. echo wchc | nc 127.0.0.1 2181 ,经过 session 列出服务器 watch 的详细信息,它的输出是一个与 watch 相关的会话的列表。
11. echo wchp | nc 127.0.0.1 2181 ,经过路径列出服务器 watch 的详细信息。它输出一个与 session 相关的路径。

 

五.Zookeeper常见应用场景

 

  • 统一命名服务(Name Service)

 

分布式应用中,一般须要有一套完整的命名规则,既可以产生惟一的名称又便于人识别和记住,一般状况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构,既对人友好又不会重复。说到这里你可能想到了 JNDI,没错 Zookeeper 的 Name Service 与 JNDI 可以完成的功能是差很少的,它们都是将有层次的目录结构关联到必定资源上,可是 Zookeeper 的 Name Service 更加是普遍意义上的关联,也许你并不须要将名称关联到特定资源上,你可能只须要一个不会重复名称,就像数据库中产生一个惟一的数字主键同样。

 

Name Service 已是 Zookeeper 内置的功能,你只要调用 Zookeeper 的 API 就能实现。如调用 create 接口就能够很容易建立一个目录节点。

 

  • 配置管理(Configuration Management)

 

配置的管理在分布式应用环境中很常见,例如同一个应用系统须要多台 PC Server 运行,可是它们运行的应用系统的某些配置项是相同的,若是要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server,这样很是麻烦并且容易出错。

 

像这样的配置信息彻底能够交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,而后将全部须要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,而后从 Zookeeper 获取新的配置信息应用到系统中。

 

图 2. 配置管理结构图

图 2. 配置管理结构图

  • 集群管理(Group Membership)

 

Zookeeper 可以很容易的实现集群管理的功能,若有多台 Server 组成一个服务集群,那么必需要一个“总管”知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它集群必须知道,从而作出调整从新分配服务策略。一样当增长集群的服务能力时,就会增长一台或多台 Server,一样也必须让“总管”知道。

 

Zookeeper 不只可以帮你维护当前的集群中机器的服务状态,并且可以帮你选出一个“总管”,让这个总管来管理集群,这就是 Zookeeper 的另外一个功能 Leader Election。

 

它们的实现方式都是在 Zookeeper 上建立一个 EPHEMERAL 类型的目录节点,而后每一个 Server 在它们建立目录节点的父目录节点上调用getChildren(String path, boolean watch) 方法并设置 watch 为 true,因为是 EPHEMERAL 目录节点,当建立它的 Server 死去,这个目录节点也随之被删除,因此 Children 将会变化,这时 getChildren上的 Watch 将会被调用,因此其它 Server 就知道已经有某台 Server 死去了。新增 Server 也是一样的原理。

 

Zookeeper 如何实现 Leader Election,也就是选出一个 Master Server。和前面的同样每台 Server 建立一个 EPHEMERAL 目录节点,不一样的是它仍是一个 SEQUENTIAL 目录节点,因此它是个 EPHEMERAL_SEQUENTIAL 目录节点。之因此它是 EPHEMERAL_SEQUENTIAL 目录节点,是由于咱们能够给每台 Server 编号,咱们能够选择当前是最小编号的 Server 为 Master,假如这个最小编号的 Server 死去,因为是 EPHEMERAL 节点,死去的 Server 对应的节点也被删除,因此当前的节点列表中又出现一个最小编号的节点,咱们就选择这个节点为当前 Master。这样就实现了动态选择 Master,避免了传统意义上单 Master 容易出现单点故障的问题。

 

图 3. 集群管理结构图

图 3. 集群管理结构图

这部分的示例代码以下,完整的代码请看附件:

 

清单 3. Leader Election 关键代码

 

 void findLeader() throws InterruptedException { 
        byte[] leader = null; 
        try { 
            leader = zk.getData(root + "/leader", true, null); 
        } catch (Exception e) { 
            logger.error(e); 
        } 
        if (leader != null) { 
            following(); 
        } else { 
            String newLeader = null; 
            try { 
                byte[] localhost = InetAddress.getLocalHost().getAddress(); 
                newLeader = zk.create(root + "/leader", localhost, 
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); 
            } catch (Exception e) { 
                logger.error(e); 
            } 
            if (newLeader != null) { 
                leading(); 
            } else { 
                mutex.wait(); 
            } 
        } 
    }

 

 

  • 共享锁(Locks)

 

共享锁在同一个进程中很容易实现,可是在跨进程或者在不一样 Server 之间就很差实现了。Zookeeper 却很容易实现这个功能,实现方式也是须要得到锁的 Server 建立一个 EPHEMERAL_SEQUENTIAL 目录节点,而后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是否是就是本身建立的目录节点,若是正是本身建立的,那么它就得到了这个锁,若是不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到本身建立的节点是列表中最小编号的目录节点,从而得到锁,释放锁很简单,只要删除前面它本身所建立的目录节点就好了。

 

图 4. Zookeeper 实现 Locks 的流程图

图 4. Zookeeper 实现 Locks 的流程图

同步锁的实现代码以下,完整的代码请看附件:

 

清单 4. 同步锁的关键代码

 

 void getLock() throws KeeperException, InterruptedException{ 
        List<String> list = zk.getChildren(root, false); 
        String[] nodes = list.toArray(new String[list.size()]); 
        Arrays.sort(nodes); 
        if(myZnode.equals(root+"/"+nodes[0])){ 
            doAction(); 
        } 
        else{ 
            waitForLock(nodes[0]); 
        } 
    } 
    void waitForLock(String lower) throws InterruptedException, KeeperException {
        Stat stat = zk.exists(root + "/" + lower,true); 
        if(stat != null){ 
            mutex.wait(); 
        } 
        else{ 
            getLock(); 
        } 
    }

 

 

  • 队列管理

 

Zookeeper 能够处理两种类型的队列:

 

  1. 当一个队列的成员都聚齐时,这个队列才可用,不然一直等待全部成员到达,这种是同步队列。
  2. 队列按照 FIFO 方式进行入队和出队操做,例如实现生产者和消费者模型。

 

同步队列用 Zookeeper 实现的实现思路以下:

 

建立一个父目录 /synchronizing,每一个成员都监控标志(Set Watch)位目录 /synchronizing/start 是否存在,而后每一个成员都加入这个队列,加入队列的方式就是建立 /synchronizing/member_i 的临时目录节点,而后每一个成员获取 / synchronizing 目录的全部目录节点,也就是 member_i。判断 i 的值是否已是成员的个数,若是小于成员个数等待 /synchronizing/start 的出现,若是已经相等就建立 /synchronizing/start。

 

用下面的流程图更容易理解:

 

图 5. 同步队列流程图

图 5. 同步队列流程图

同步队列的关键代码以下,完整的代码请看附件:

 

清单 5. 同步队列

 

 void addQueue() throws KeeperException, InterruptedException{ 
        zk.exists(root + "/start",true); 
        zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE, 
        CreateMode.EPHEMERAL_SEQUENTIAL); 
        synchronized (mutex) { 
            List<String> list = zk.getChildren(root, false); 
            if (list.size() < size) { 
                mutex.wait(); 
            } else { 
                zk.create(root + "/start", new byte[0], Ids.OPEN_ACL_UNSAFE,
                 CreateMode.PERSISTENT); 
            } 
        } 
 }

 

 

当队列没尽是进入 wait(),而后会一直等待 Watch 的通知,Watch 的代码以下:

 

 public void process(WatchedEvent event) { 
        if(event.getPath().equals(root + "/start") &&
         event.getType() == Event.EventType.NodeCreated){ 
            System.out.println("获得通知"); 
            super.process(event); 
            doAction(); 
        } 
    }

 

 

FIFO 队列用 Zookeeper 实现思路以下:

 

实现的思路也很是简单,就是在特定的目录下建立 SEQUENTIAL 类型的子目录 /queue_i,这样就能保证全部成员加入队列时都是有编号的,出队列时经过 getChildren( ) 方法能够返回当前全部的队列中的元素,而后消费其中最小的一个,这样就能保证 FIFO。

 

下面是生产者和消费者这种队列形式的示例代码,完整的代码请看附件:

 

清单 6. 生产者代码

 

 boolean produce(int i) throws KeeperException, InterruptedException{ 
        ByteBuffer b = ByteBuffer.allocate(4); 
        byte[] value; 
        b.putInt(i); 
        value = b.array(); 
        zk.create(root + "/element", value, ZooDefs.Ids.OPEN_ACL_UNSAFE, 
                    CreateMode.PERSISTENT_SEQUENTIAL); 
        return true; 
    }

 

 

清单 7. 消费者代码

 

 int consume() throws KeeperException, InterruptedException{ 
        int retvalue = -1; 
        Stat stat = null; 
        while (true) { 
            synchronized (mutex) { 
                List<String> list = zk.getChildren(root, true); 
                if (list.size() == 0) { 
                    mutex.wait(); 
                } else { 
                    Integer min = new Integer(list.get(0).substring(7)); 
                    for(String s : list){ 
                        Integer tempValue = new Integer(s.substring(7)); 
                        if(tempValue < min) min = tempValue; 
                    } 
                    byte[] b = zk.getData(root + "/element" + min,false, stat); 
                    zk.delete(root + "/element" + min, 0); 
                    ByteBuffer buffer = ByteBuffer.wrap(b); 
                    retvalue = buffer.getInt(); 
                    return retvalue; 
                } 
            } 
        } 
 }

部份内容参考:http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/ 

 

六.Zookeeper基本原理

1. 数据模型
zookeeper-tree
如上图所示,ZooKeeper数据模型的结构与Unix文件系统很相似,总体上能够看做是一棵树,每一个节点称作一个ZNode。每一个ZNode均可以经过其路径惟一标识,好比上图中第三层的第一个ZNode, 它的路径是/app1/c1。在每一个ZNode上可存储少许数据(默认是1M, 能够经过配置修改, 一般不建议在ZNode上存储大量的数据),这个特性很是有用,在后面的典型应用场景中会介绍到。另外,每一个ZNode上还存储了其Acl信息,这里须要注意,虽然说ZNode的树形结构跟Unix文件系统很相似,可是其Acl与Unix文件系统是彻底不一样的,每一个ZNode的Acl的独立的,子结点不会继承父结点的。

2.重要概念 
2.1 ZNode
前文已介绍了ZNode, ZNode根据其自己的特性,能够分为下面两类:

  • Regular ZNode: 常规型ZNode, 用户须要显式的建立、删除
  • Ephemeral ZNode: 临时型ZNode, 用户建立它以后,能够显式的删除,也能够在建立它的Session结束后,由ZooKeeper Server自动删除

ZNode还有一个Sequential的特性,若是建立的时候指定的话,该ZNode的名字后面会自动Append一个不断增长的SequenceNo。

2.2 Session
Client与ZooKeeper之间的通讯,须要建立一个Session,这个Session会有一个超时时间。由于ZooKeeper集群会把Client的Session信息持久化,因此在Session没超时以前,Client与ZooKeeper Server的链接能够在各个ZooKeeper Server之间透明地移动。

在实际的应用中,若是Client与Server之间的通讯足够频繁,Session的维护就不须要其它额外的消息了。不然,ZooKeeper Client会每t/3 ms发一次心跳给Server,若是Client 2t/3 ms没收到来自Server的心跳回应,就会换到一个新的ZooKeeper Server上。这里t是用户配置的Session的超时时间。

2.3 Watcher
ZooKeeper支持一种Watch操做,Client能够在某个ZNode上设置一个Watcher,来Watch该ZNode上的变化。若是该ZNode上有相应的变化,就会触发这个Watcher,把相应的事件通知给设置Watcher的Client。须要注意的是,ZooKeeper中的Watcher是一次性的,即触发一次就会被取消,若是想继续Watch的话,须要客户端从新设置Watcher。这个跟epoll里的oneshot模式有点相似。

3. ZooKeeper特性 
3.1 读、写(更新)模式
在ZooKeeper集群中,读能够从任意一个ZooKeeper Server读,这一点是保证ZooKeeper比较好的读性能的关键;写的请求会先Forwarder到Leader,而后由Leader来经过ZooKeeper中的原子广播协议,将请求广播给全部的Follower,Leader收到一半以上的写成功的Ack后,就认为该写成功了,就会将该写进行持久化,并告诉客户端写成功了。

3.2 WAL和Snapshot
和大多数分布式系统同样,ZooKeeper也有WAL(Write-Ahead-Log),对于每个更新操做,ZooKeeper都会先写WAL, 而后再对内存中的数据作更新,而后向Client通知更新结果。另外,ZooKeeper还会按期将内存中的目录树进行Snapshot,落地到磁盘上,这个跟HDFS中的FSImage是比较相似的。这么作的主要目的,一固然是数据的持久化,二是加快重启以后的恢复速度,若是所有经过Replay WAL的形式恢复的话,会比较慢。

3.3 FIFO
对于每个ZooKeeper客户端而言,全部的操做都是遵循FIFO顺序的,这一特性是由下面两个基本特性来保证的:一是ZooKeeper Client与Server之间的网络通讯是基于TCP,TCP保证了Client/Server之间传输包的顺序;二是ZooKeeper Server执行客户端请求也是严格按照FIFO顺序的。

3.4 Linearizability
在ZooKeeper中,全部的更新操做都有严格的偏序关系,更新操做都是串行执行的,这一点是保证ZooKeeper功能正确性的关键。

ZooKeeper Client API

ZooKeeper Client Library提供了丰富直观的API供用户程序使用,下面是一些经常使用的API:

  • create(path, data, flags): 建立一个ZNode, path是其路径,data是要存储在该ZNode上的数据,flags经常使用的有: PERSISTEN, PERSISTENT_SEQUENTAIL, EPHEMERAL, EPHEMERAL_SEQUENTAIL
  • delete(path, version): 删除一个ZNode,能够经过version删除指定的版本, 若是version是-1的话,表示删除全部的版本
  • exists(path, watch): 判断指定ZNode是否存在,并设置是否Watch这个ZNode。这里若是要设置Watcher的话,Watcher是在建立ZooKeeper实例时指定的,若是要设置特定的Watcher的话,能够调用另外一个重载版本的exists(path, watcher)。如下几个带watch参数的API也都相似
  • getData(path, watch): 读取指定ZNode上的数据,并设置是否watch这个ZNode
  • setData(path, watch): 更新指定ZNode的数据,并设置是否Watch这个ZNode
  • getChildren(path, watch): 获取指定ZNode的全部子ZNode的名字,并设置是否Watch这个ZNode
  • sync(path): 把全部在sync以前的更新操做都进行同步,达到每一个请求都在半数以上的ZooKeeper Server上生效。path参数目前没有用
  • setAcl(path, acl): 设置指定ZNode的Acl信息
  • getAcl(path): 获取指定ZNode的Acl信息

ZooKeeper典型应用场景

1. 名字服务(NameService) 
分布式应用中,一般须要一套完备的命令机制,既能产生惟一的标识,又方便人识别和记忆。 咱们知道,每一个ZNode均可以由其路径惟一标识,路径自己也比较简洁直观,另外ZNode上还能够存储少许数据,这些都是实现统一的NameService的基础。下面以在HDFS中实现NameService为例,来讲明实现NameService的基本布骤:

  • 目标:经过简单的名字来访问指定的HDFS机群
  • 定义命名规则:这里要作到简洁易记忆。下面是一种可选的方案: [serviceScheme://][zkCluster]-[clusterName],好比hdfs://lgprc-example/表示基于lgprc ZooKeeper集群的用来作example的HDFS集群
  • 配置DNS映射: 将zkCluster的标识lgprc经过DNS解析到对应的ZooKeeper集群的地址
  • 建立ZNode: 在对应的ZooKeeper上建立/NameService/hdfs/lgprc-example结点,将HDFS的配置文件存储于该结点下
  • 用户程序要访问hdfs://lgprc-example/的HDFS集群,首先经过DNS找到lgprc的ZooKeeper机群的地址,而后在ZooKeeper的/NameService/hdfs/lgprc-example结点中读取到HDFS的配置,进而根据获得的配置,获得HDFS的实际访问入口

2. 配置管理(Configuration Management) 
在分布式系统中,常会遇到这样的场景: 某个Job的不少个实例在运行,它们在运行时大多数配置项是相同的,若是想要统一改某个配置,一个个实例去改,是比较低效,也是比较容易出错的方式。经过ZooKeeper能够很好的解决这样的问题,下面的基本的步骤:

  • 将公共的配置内容放到ZooKeeper中某个ZNode上,好比/service/common-conf
  • 全部的实例在启动时都会传入ZooKeeper集群的入口地址,而且在运行过程当中Watch /service/common-conf这个ZNode
  • 若是集群管理员修改了了common-conf,全部的实例都会被通知到,根据收到的通知更新本身的配置,并继续Watch /service/common-conf

3. 组员管理(Group Membership) 
在典型的Master-Slave结构的分布式系统中,Master须要做为“总管”来管理全部的Slave, 当有Slave加入,或者有Slave宕机,Master都须要感知到这个事情,而后做出对应的调整,以便不影响整个集群对外提供服务。以HBase为例,HMaster管理了全部的RegionServer,当有新的RegionServer加入的时候,HMaster须要分配一些Region到该RegionServer上去,让其提供服务;当有RegionServer宕机时,HMaster须要将该RegionServer以前服务的Region都从新分配到当前正在提供服务的其它RegionServer上,以便不影响客户端的正常访问。下面是这种场景下使用ZooKeeper的基本步骤:

  • Master在ZooKeeper上建立/service/slaves结点,并设置对该结点的Watcher
  • 每一个Slave在启动成功后,建立惟一标识本身的临时性(Ephemeral)结点/service/slaves/${slave_id},并将本身地址(ip/port)等相关信息写入该结点
  • Master收到有新子结点加入的通知后,作相应的处理
  • 若是有Slave宕机,因为它所对应的结点是临时性结点,在它的Session超时后,ZooKeeper会自动删除该结点
  • Master收到有子结点消失的通知,作相应的处理

4. 简单互斥锁(Simple Lock) 
咱们知识,在传统的应用程序中,线程、进程的同步,均可以经过操做系统提供的机制来完成。可是在分布式系统中,多个进程之间的同步,操做系统层面就无能为力了。这时候就须要像ZooKeeper这样的分布式的协调(Coordination)服务来协助完成同步,下面是用ZooKeeper实现简单的互斥锁的步骤,这个能够和线程间同步的mutex作类比来理解:

  • 多个进程尝试去在指定的目录下去建立一个临时性(Ephemeral)结点 /locks/my_lock
  • ZooKeeper能保证,只会有一个进程成功建立该结点,建立结点成功的进程就是抢到锁的进程,假设该进程为A
  • 其它进程都对/locks/my_lock进行Watch
  • 当A进程再也不须要锁,能够显式删除/locks/my_lock释放锁;或者是A进程宕机后Session超时,ZooKeeper系统自动删除/locks/my_lock结点释放锁。此时,其它进程就会收到ZooKeeper的通知,并尝试去建立/locks/my_lock抢锁,如此循环反复

5. 互斥锁(Simple Lock without Herd Effect) 
上一节的例子中有一个问题,每次抢锁都会有大量的进程去竞争,会形成羊群效应(Herd Effect),为了解决这个问题,咱们能够经过下面的步骤来改进上述过程:

  • 每一个进程都在ZooKeeper上建立一个临时的顺序结点(Ephemeral Sequential) /locks/lock_${seq}
  • ${seq}最小的为当前的持锁者(${seq}是ZooKeeper生成的Sequenctial Number)
  • 其它进程都对只watch比它次小的进程对应的结点,好比2 watch 1, 3 watch 2, 以此类推
  • 当前持锁者释放锁后,比它次大的进程就会收到ZooKeeper的通知,它成为新的持锁者,如此循环反复

这里须要补充一点,一般在分布式系统中用ZooKeeper来作Leader Election(选主)就是经过上面的机制来实现的,这里的持锁者就是当前的“主”。

6. 读写锁(Read/Write Lock) 
咱们知道,读写锁跟互斥锁相比不一样的地方是,它分红了读和写两种模式,多个读能够并发执行,但写和读、写都互斥,不能同时执行行。利用ZooKeeper,在上面的基础上,稍作修改也能够实现传统的读写锁的语义,下面是基本的步骤:

  • 每一个进程都在ZooKeeper上建立一个临时的顺序结点(Ephemeral Sequential) /locks/lock_${seq}
  • ${seq}最小的一个或多个结点为当前的持锁者,多个是由于多个读能够并发
  • 须要写锁的进程,Watch比它次小的进程对应的结点
  • 须要读锁的进程,Watch比它小的最后一个写进程对应的结点
  • 当前结点释放锁后,全部Watch该结点的进程都会被通知到,他们成为新的持锁者,如此循环反复

7. 屏障(Barrier) 
在分布式系统中,屏障是这样一种语义: 客户端须要等待多个进程完成各自的任务,而后才能继续往前进行下一步。下用是用ZooKeeper来实现屏障的基本步骤:

  • Client在ZooKeeper上建立屏障结点/barrier/my_barrier,并启动执行各个任务的进程
  • Client经过exist()来Watch /barrier/my_barrier结点
  • 每一个任务进程在完成任务后,去检查是否达到指定的条件,若是没达到就啥也不作,若是达到了就把/barrier/my_barrier结点删除
  • Client收到/barrier/my_barrier被删除的通知,屏障消失,继续下一步任务

8. 双屏障(Double Barrier)
双屏障是这样一种语义: 它能够用来同步一个任务的开始和结束,当有足够多的进程进入屏障后,才开始执行任务;当全部的进程都执行完各自的任务后,屏障才撤销。下面是用ZooKeeper来实现双屏障的基本步骤:

        进入屏障:

 

  • Client Watch /barrier/ready结点, 经过判断该结点是否存在来决定是否启动任务
  • 每一个任务进程进入屏障时建立一个临时结点/barrier/process/${process_id},而后检查进入屏障的结点数是否达到指定的值,若是达到了指定的值,就建立一个/barrier/ready结点,不然继续等待
  • Client收到/barrier/ready建立的通知,就启动任务执行过程
        离开屏障:

 

  • Client Watch /barrier/process,若是其没有子结点,就能够认为任务执行结束,能够离开屏障 
  • 每一个任务进程执行任务结束后,都须要删除本身对应的结点/barrier/process/${process_id} 

 

综上,Zookeeper 的更多知识,在深刻学习和使用以后,再作补充。

相关文章
相关标签/搜索