1、简介Apache Zookeeper 是由 Apache Hadoop 的 Zookeeper 子项目发展而来,现在已经成为了 Apache 的顶级项目。Zookeeper 为分布式系统提供了高效可靠且易于使用的协同服务,它可以为分布式应用提供相当多的服务,诸如统一命名服务,配置管理,状态同步和组服务等。 Zookeeper 接口简单,开发人员不必过多地纠结在分布式系统编程难于处理的同步和一致性问题上,你可以使用 Zookeeper 提供的现成(off-the-shelf)服务来实现分布式系统的配置管理,组管理,Leader 选举等功能。Zookeeper 维护了大规模分布式系统中的常用对象,
2、比如配置信息,层次化命名空间等,本文将从开发者的角度详细介绍 Zookeeper 的配置信息的意义以及 Zookeeper 的典型应用场景(配置文件的管理、集群管理、分布式队列、同步锁、Leader 选举、队列管理等)。Zookeeper 安装与配置本文采用 Zookeeper-3.4.0 以基础介绍它的安装步骤以及配置信息,最新的代码可以到 Zookeeper 的官网: http:/zookeeper.apache.org/下载。Zookeeper 功能强大,但是安装却十分简单,下面重点以伪分布式模式来介绍 Zookeeper 的安装。伪分布式模式安装Zookeeper 安装模式包括:单机模
3、式,伪分布式模式和完全的集群模式。单机模式最简单,本文将跳过单机模式安装(单机模式安装步骤参见 Zeekeeper 官方文档:http:/zookeeper.apache.org/doc/current/zookeeperStarted.html),伪分布式模式与集群模式配置差别不大,由于手头机器有限,所以本文采用了在单台机器上伪分布式安装。本文在 Ubuntu 12.04 上操作,Java 环境为 OpenJDK 1.7。安装 Zookeeper 前首先下载你需要的版本,暂时解压到指定目录(本文解压至/zookeeper/目录下),并修改配置(可能需要多次修改配置文件),本次伪分布式模拟 5
4、 个 Zookeeper 节点,事先在/tmpzookeeper 目录下建立 5 个文件夹,分别命名 为:server001,server002,server003,server004,server005,然后在每个server00#文件夹下面新 建 data 和 logs 子文件夹。Zookeeper 的配置文件主要在 conf 目录,包括 zoo.cfg (zoo_sample.cfg)和log4j.properties,修改 zoo_sample.cfg,重命名为 zoo.cgf,打开zoo.cfg,内容如下:# The number of milliseconds of each ti
5、cktickTime=2000# The number of ticks that the initial # synchronization phase can takeinitLimit=10# The number of ticks that can pass between # sending a request and getting an acknowledgementsyncLimit=5# the directory where the snapshot is stored.# do not use /tmp for storage, /tmp here is just # e
6、xample sakes.dataDir=/tmp/zookeeper# the port at which the clients will connectclientPort=2181# 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 ret
7、ain in dataDir#autopurge.snapRetainCount=3# Purge task interval in hours# Set to “0“ to disable auto purge feature#autopurge.purgeInterval=1将内容修改为(server001 节点的配置文件):# The number of milliseconds of each ticktickTime=2000# The number of ticks that the initial # synchronization phase can takeinitLimit
8、=10# The number of ticks that can pass between # sending a request and getting an acknowledgementsyncLimit=5# the directory where the snapshot is stored.# do not use /tmp for storage, /tmp here is just # example sakes.dataDir=/tmp/zookeeper/server001/datadataLogDir=/tmp/zookeeper/server001/logs# the
9、 port at which the clients will connectclientPort=2181# 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
10、=3# Purge task interval in hours# Set to “0“ to disable auto purge feature#autopurge.purgeInterval=1server.1=127.0.0.1:8881:7771server.2=127.0.0.1:8882:7772server.3=127.0.0.1:8883:7773server.4=127.0.0.1:8884:7774server.5=127.0.0.1:8885:7775 tickTime:这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 t
11、ickTime 时间就会发送一个心跳。 dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。 clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。 initLimit:这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超
12、过 5 个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 5*2000=10 秒 syncLimit:这个配置项标识 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 2*2000=4 秒 server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器; B 是这个服务器的 ip 地址; C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是万一集群中的 Leader 服
13、务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口。如果是伪集群的配置方式,由于 B 都是一样,所以不同的 Zookeeper 实例通信端口号不能一样,所以要给它们分配不同的端口号。然后将此 zookeeper 包拷贝至 /tmp/zookeeper/server001/ 目录下,并在 /tmp/zookeeper/server001/data/ 下建立一个 myid 文件,文件内容为 1,echo “1“ /tmp/zookeeper/server001/data/myid继续修改/zookeeper/目录中的 zookeepe
14、r 配置文件文件(server002 的配置文件,注意 clientPort=2182,与 server001 中的 clientPort=2181 不同,后续修改配置均需设置不同的 clientPort),内容如下:# The number of milliseconds of each ticktickTime=2000# The number of ticks that the initial # synchronization phase can takeinitLimit=10# The number of ticks that can pass between # sending
15、a request and getting an acknowledgementsyncLimit=5# the directory where the snapshot is stored.# do not use /tmp for storage, /tmp here is just # example sakes.dataDir=/tmp/zookeeper/server002/datadataLogDir=/tmp/zookeeper/server002/logs# the port at which the clients will connectclientPort=2182# B
16、e 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 a
17、uto purge feature#autopurge.purgeInterval=1server.1=127.0.0.1:8881:7771server.2=127.0.0.1:8882:7772server.3=127.0.0.1:8883:7773server.4=127.0.0.1:8884:7774server.5=127.0.0.1:8885:7775然后将此 zookeeper 包拷贝至 /tmp/zookeeper/server002/ 目录下,并在 /tmp/zookeeper/server002/data/ 下建立一个 myid 文件,文件内容为 2,echo “2“ /t
18、mp/zookeeper/server001/data/myid依次修改配置文件,建立 server003,server004,server005 节点文件夹,完成上述步骤后/tmp/zookeeper 目录结构如下:forhappyforhappy-lenovo:/tmp/zookeeper$ tree -d -L 2. server001 data logs zookeeper-3.4.0 server002 data logs zookeeper-3.4.0 server003 data logs zookeeper-3.4.0 server004 data logs zookeeper
19、-3.4.0 server005 data logs zookeeper-3.4.0然后依次进入每个文件夹节点的 zookeeper 目录中,启动 zookeeper 服务,$ bin/zkServer.sh start如果一切顺利,Zookeeper 伪分布式模式安装成功,下面验证 Zookeeper 安装的正确性。进入任意一个文件夹节点的 zookeeper 包所在的目录,执行一下命令:$ bin/zkCli.sh -server 127.0.0.1:2181执行成功后:forhappyforhappy-lenovo:/tmp/zookeeper/server001/zookeeper-3
20、.4.0$ bin/zkCli.sh -server 127.0.0.1:2181Connecting to 127.0.0.1:2181Welcome to ZooKeeper!WATCHER:WatchedEvent state:SyncConnected type:None path:nullzk: 127.0.0.1:2181(CONNECTED) 0help 帮助:zk: 127.0.0.1:2181(CONNECTED) 0 helpZooKeeper -server host:port cmd argsconnect host:portget path watchls path
21、watchset path data versionrmr pathdelquota -n|-b pathquit printwatches on|offcreate -s -e path data aclstat path watchclose ls2 path watchhistory listquota pathsetAcl path aclgetAcl pathsync pathredo cmdnoaddauth scheme authdelete path versionsetquota -n|-b val path至此,Zookeeper 安装完成,下一篇博客将介绍 Zookeep
22、er Java API,并给出 Zookeeper 典型的应用场景。简介Apache Zookeeper 是由 Apache Hadoop 的 Zookeeper 子项目发展而来,现在已经成为了 Apache 的顶级项目。Zookeeper 为分布式系统提供了高效可靠且易于使用的协同服务,它可以为分布式应用提供相当多的服务,诸如统一命名服务,配置管理,状态同步和组服务等。 Zookeeper 接口简单,开发人员不必过多地纠结在分布式系统编程难于处理的同步和一致性问题上,你可以使用 Zookeeper 提供的现成(off-the-shelf)服务来实现分布式系统的配置管理,组管理,Leader
23、选举等功能。Zookeeper 维护了大规模分布式系统中的常用对象,比如配置信息,层次化命名空间等,本文将从开发者的角度详细介绍 Zookeeper 的配置信息的意义以及 Zookeeper 的典型应用场景(配置文件的管理、集群管理、分布式队列、同步锁、Leader 选举、队列管理等)。上一篇博客主要讲了 Apache Zookeeper 的安装与配置,本文主要介绍 Zookeeper Java API。Zookeeper 提供了原生的 Java API,另外还有 C 调用接口,本文暂不介绍Zookeeper 的 C API。Zookeeper Java APIZookeeper 作为一个分布
24、式服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储,但是 Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。Zookeeper 客户端在连接 Zookeeper 服务器需要实例化一个org.apache.zookeeper.ZooKeeper 对象,然后调用该类提供的接口与Zookeeper 服务器进行交互。如果不指明,该类的所有方法均是线程安全的。一旦 Zookeeper 客户端与服务器建立连接,客户端就会被分配一个会话ID(
25、session ID),客户端会定期向服务器端发送心跳以保持该会话有效。只要客户端会话有效,应用程序可以调用 Zookeeper 客户端的接口与服务器端进行交互。下表主要介绍了 org.apache.zookeeper. ZooKeeper 方法( 该表摘自 IBM Developerworks: 分布式服务框架 Zookeeper - 管理分布式环境中的数据)方法名 方法功能描述String create(String path, byte data, List acl, CreateMode createMode)创建一个给定的目录节点 path, 并给它设置数据,CreateMode 标
26、 识有四种形式的目录节点,分别是 PERSISTENT:持久化目录节点,这个目录节点存储的数据不会丢失;PERSISTENT_SEQUENTIAL:顺序自动编号的目录节点,这种目 录节点会根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名;EPHEMERAL:临时目录节点,一旦创建这个节点的客户端与服务器端口也就是 session 超时,这种节点会被自动删除;EPHEMERAL_SEQUENTIAL:临时自动编号节点。Stat exists(String path,boolean watch)判断某个 path 是否存在,并设置是否监控这个目录节点,这里的 watch
27、er 是在创建 ZooKeeper 实例时指定的 watcher,exists 方法还有一个重载方法,可以指定特定的 watcher。Stat exists(String path, Watcher watcher) 重载方法,这里给 某个目录节点设置特定的 watcher,Watcher 在 ZooKeeper 是一个核心功能,Watcher 可以监控目录节点的数据变化以及子目录的变化,一旦这些状态发生变化,服务器就会通知所有设置在这个目录节点上的 Watcher,从而每个客户端都很快知道它所关注的目录节点的状态发生变化,而做出相应的反应。void delete(String path, i
28、nt version) 删除 path 对应的目录节点,version 为 -1 可以匹配任何版本,也就删除了这个目录节点所有数据。List getChildren(String path, boolean watch)获取指定 path 下的所有子目录节点,同样 getChildren方法也有一个重载方法可以设置特定的 watcher 监控子节点的状态。Stat setData(String path, byte data, int version)给 path 设置数据,可以指定这个数据的版本号,如果 version 为 -1 怎可以匹配任何版本。byte getData(String p
29、ath, boolean watch, Stat stat)获取这个 path 对应的目录节点存储的数据,数据的版本等信息可以通过 stat 来指定,同时还可以设置是否监控这个目录节点数据的状态。void addAuthInfo(String scheme,byte auth) 客户端将自己的授权信息提交给服务器,服务器将根据这个授权信息验证客户端的访问权限。Stat setACL(String path, List acl, int version)给某个目录节点重新设置访问权限,需要注意的是 Zookeeper 中的目录节点权限不具有传递性,父目录节点的权限不能传递给子目录节点。目录节点
30、ACL 由两部分组成:perms 和 id。Perms 有 ALL、READ、WRITE、CREATE、DELETE、ADMIN 几种 而 id 标识了访问目录节点的身份列表,默认情况下有以下两种:ANYONE_ID_UNSAFE = new Id(“world“, “anyone“) 和 AUTH_IDS = new Id(“auth“, “) 分别表示任何人都可以访问和创建者拥有访问权限。 List getACL(String path, Stat stat) 获取某个目录节点的访问权限列表 除了以上这些上表中列出的方法之外还有一些重载方法,如都提供了一个回调类的重载方法以及可以设置特定
31、Watcher 的重载方法,具体的方法可以参考 org.apache.zookeeper. ZooKeeper 类的 API 说明。下面给出 Java API 的基本操作:/ 创建一个与服务器的连接ZooKeeper zk = new ZooKeeper(“localhost:“ + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT, new Watcher() / 监控所有被触发的事件public void process(WatchedEvent event) System.out.println(“已经触发了“ + event.getType() +
32、“事件!“); ); / 创建一个目录节点zk.create(“/testRootPath“, “testRootData“.getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); / 创建一个子目录节点zk.create(“/testRootPath/testChildPathOne“, “testChildDataOne“.getBytes(),Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); System.out.println(new String(zk.getData(“/testRootP
33、ath“,false,null); / 取出子目录节点列表System.out.println(zk.getChildren(“/testRootPath“,true); / 修改子目录节点数据zk.setData(“/testRootPath/testChildPathOne“,“modifyChildDataOne“.getBytes(),-1); System.out.println(“目录节点状态:“+zk.exists(“/testRootPath“,true)+“); / 创建另外一个子目录节点zk.create(“/testRootPath/testChildPathTwo“,
34、“testChildDataTwo“.getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); System.out.println(new String(zk.getData(“/testRootPath/testChildPathTwo“,true,null); / 删除子目录节点zk.delete(“/testRootPath/testChildPathTwo“,-1); zk.delete(“/testRootPath/testChildPathOne“,-1); / 删除父目录节点zk.delete(“/testRootPath“
35、,-1); / 关闭连接zk.close();简介Apache Zookeeper 是由 Apache Hadoop 的 Zookeeper 子项目发展而来,现在已经成为了 Apache 的顶级项目。Zookeeper 为分布式系统提供了高效可靠且易于使用的协同服务,它可以为分布式应用提供相当多的服务,诸如统一命名服务,配置管理,状态同步和组服务等。 Zookeeper 接口简单,开发人员不必过多地纠结在分布式系统编程难于处理的同步和一致性问题上,你可以使用 Zookeeper 提供的现成(off-the-shelf)服务来实现分布式系统的配置管理,组管理,Leader 选举等功能。英文原文地
36、址:http:/zookeeper.apache.org/doc/current/javaExample.html一个简单的 Zookeeper Watch 客户端为了介绍 Zookeeper Java API 的基本用法,本文将带你如何一步一步实现一个功能简单的 Zookeeper 客户端。该 Zookeeper 客户端会监视一个你指定 Zookeeper 节点 Znode, 当被监视的节点发生变化时,客户端会启动或者停止某一程序。基本要求该客户端具备四个基本要求: 客户端所带参数: o Zookeeper 服务地址。o 被监视的 Znode 节点名称。o 可执行程序及其所带的参数 客户端会
37、获取被监视 Znode 节点的数据并启动你所指定的可执行程序。 如果被监视的 Znode 节点发生改变,客户端重新获取其内容并再次启动你所指定的可执行程序。 如果被监视的 Znode 节点消失,客户端会杀死可执行程序。程序设计一般而言,Zookeeper 应用程序分为两部分,其中一部分维护与服务器端的连接,另外一部分监视 Znode 节点的数据。在本程序中,Executor 类负责维护 Zookeeper 连接,DataMonitor 类监视 Zookeeper 目录树中的数据, 同时,Executor 包含了主线程和程序主要的执行逻辑,它负责少量的用户交互,以及与可执行程序的交互,该可执行程
38、序接受你向它传入的参数,并且会根据被监视的 Znode 节点的状态变化停止或重启。Executor 类Executor 对象是本例程最基本的“容器”,它包括 Zookeeper 对象和DataMonitor 对象。public static void main(String args) if (args.length 0) os.write(b, 0, rc); catch (IOException e) public void exists(byte data) if (data = null) if (child != null) System.out.println(“Killing p
39、rocess“);child.destroy();try child.waitFor(); catch (InterruptedException e) child = null; else if (child != null) System.out.println(“Stopping child“);child.destroy();try child.waitFor(); catch (InterruptedException e) e.printStackTrace();try FileOutputStream fos = new FileOutputStream(filename);fo
40、s.write(data);fos.close(); catch (IOException e) e.printStackTrace();try System.out.println(“Starting child“);child = Runtime.getRuntime().exec(exec);new StreamWriter(child.getInputStream(), System.out);new StreamWriter(child.getErrorStream(), System.err); catch (IOException e) e.printStackTrace();D
41、ataMonitor.java:/* A simple class that monitors the data and existence of a ZooKeeper* node. It uses asynchronous ZooKeeper APIs.*/import java.util.Arrays;import org.apache.zookeeper.KeeperException;import org.apache.zookeeper.WatchedEvent;import org.apache.zookeeper.Watcher;import org.apache.zookee
42、per.ZooKeeper;import org.apache.zookeeper.AsyncCallback.StatCallback;import org.apache.zookeeper.KeeperException.Code;import org.apache.zookeeper.data.Stat;public class DataMonitor implements Watcher, StatCallback ZooKeeper zk;String znode;Watcher chainedWatcher;boolean dead;DataMonitorListener list
43、ener;byte prevData;public DataMonitor(ZooKeeper zk, String znode, Watcher chainedWatcher,DataMonitorListener listener) this.zk = zk;this.znode = znode;this.chainedWatcher = chainedWatcher;this.listener = listener;/ Get things started by checking if the node exists. We are going/ to be completely eve
44、nt drivenzk.exists(znode, true, this, null);/* Other classes use the DataMonitor by implementing this method*/public interface DataMonitorListener /* The existence status of the node has changed.*/void exists(byte data);/* The ZooKeeper session is no longer valid.* param rc* the ZooKeeper reason cod
45、e*/void closing(int rc);public void process(WatchedEvent event) String path = event.getPath();if (event.getType() = Event.EventType.None) / We are are being told that the state of the/ connection has changedswitch (event.getState() case SyncConnected:/ In this particular example we dont need to do a
46、nything/ here - watches are automatically re-registered with / server and any watches triggered while the client was / disconnected will be delivered (in order of course)break;case Expired:/ Its all overdead = true;listener.closing(KeeperException.Code.SessionExpired);break; else if (path != null if
47、 (chainedWatcher != null) chainedWatcher.process(event);public void processResult(int rc, String path, Object ctx, Stat stat) boolean exists;switch (rc) case Code.Ok:exists = true;break;case Code.NoNode:exists = false;break;case Code.SessionExpired:case Code.NoAuth:dead = true;listener.closing(rc);r
48、eturn;default:/ Retry errorszk.exists(znode, true, this, null);return;byte b = null;if (exists) try b = zk.getData(znode, false, null); catch (KeeperException e) / We dont need to worry about recovering now. The watch/ callbacks will kick off any exception handlinge.printStackTrace(); catch (Interru
49、ptedException e) return;if (b = null prevData = b;ZooKeeper 典型的应用场景Zookeeper 从设计模式角度来看,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生 变化,Zookeeper 就将负责通知已经在 Zookeeper 上注册的那些观察者做出相应的反应,从而实现集群中类似 Master/Slave 管理模式,关于 Zookeeper 的详细架构等内部细节可以阅读 Zookeeper 的源码下面详细介绍这些典型的应用场景,也就是 Zookeeper 到底能帮我们