第8章 ZooKeeper操作

8.1 集群环境搭建

【操作目的】
由于在ZooKeeper集群中,会有一个Leader服务器负责管理和协调其他集群服务器,因此服务器的数量通常都是单数,例如3,5,7...等,这样数量为2n+1的服务器就可以允许最多n台服务器的失效。
【操作步骤】
本例中,我们仍然使用三个节点搭建部署ZooKeeper集群,搭建步骤如下:

1.上传ZooKeeper安装文件

在centos01节点中,上传ZooKeeper安装文件zookeeper-3.4.9.tar.gz到目录/opt/softwares/中,并将其解压到目录/opt/modules/,解压命令如下:

tar -zxvf zookeeper-3.4.9.tar.gz -C /opt/modules/

2.编写配置文件

(1)在ZooKeeper安装目录下新建文件夹dataDir,用于存放ZooKeeper数据。
(2)在ZooKeeper安装目录下的conf文件夹中新建配置文件zoo.cfg,加入以下内容:

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/modules/zookeeper-3.4.9/dataDir
clientPort=2181

server.1=centos01:2888:3888
server.2=centos02:2888:3888
server.3=centos03:2888:3888

上述参数说明如下:
initLimit:集群中的Follower服务器初始化连接Leader服务器时能等待的最大心跳数(连接超时时长)。默认为10,即如果经过10个心跳之后Follower服务器仍然没有收到Leader服务器的返回信息,则连接失败。本例中该参数值为5,参数tickTime为2000,则连接超时时长为52000=10秒(即tickTimeinitLimit=10秒)。
syncLimit:集群中的Follower服务器与Leader服务器之间发送消息以及请求/应答时所能等待的最多心跳数。本例中,tickTime的值为2,时长为2*2000=4秒。
server.id:标识不同的ZooKeeper服务器。ZooKeeper可以从“server.id=host:port1:port2”中读取相关信息。其中,id值必须在整个集群中是唯一的,且大小在1到255之间;host是服务器的名称或IP地址;第一个端口(port1)是Leader端口,即该服务器作为Leader时供Follower连接的端口;第二个端口(port2)是选举端口,即选举Leader服务器时供其它Follower连接的端口。
dataDir:ZooKeeper保存数据的目录。
clientPort:客户端连接ZooKeeper服务器的端口,ZooKeeper会监听这个端口,接收客户端的请求。
(3)在配置文件zoo.cfg中的参数dataDir指定的目录下(此处为ZooKeeper安装目录下的dataDir文件夹)新建一个名为myid的文件,这个文件仅包含一行内容,即当前服务器的id值,与参数server.id中的id值相同。本例中,当前服务器(centos01)的id值为1,则应该在myid文件中写入数字1。ZooKeeper启动时会读取该文件,将其中的数据与zoo.cfg里写入的配置信息进行对比,从而获取当前服务器的身份信息。

3.拷贝ZooKeeper安装信息到其它节点

centos01节点安装完成后,需要拷贝整个ZooKeeper安装目录到centos02和centos03节点,命令如下:

scp -r /opt/modules/zookeeper-3.4.9/ hadoop@centos02:/opt/modules/
scp -r /opt/modules/zookeeper-3.4.9/ hadoop@centos03:/opt/modules/

4.修改其它节点配置

拷贝完成后,需要将centos02和centos03节点中的myid文件的值修改为对应的数字,即作出以下操作:
修改centos02节点中的opt/modules/zookeeper-3.4.9/dataDir/myid文件中的值为2。
修改centos03节点中的opt/modules/zookeeper-3.4.9/dataDir/myid文件中的值为3。

5.启动ZooKeeper

分别进入每个节点的ZooKeeper安装目录,执行如下命令:

bin/zkServer.sh start

输出以下信息代表启动成功:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED

需要注意的是,每台服务器都要执行一遍启动命令,这样才能使得整个集群启动起来。

6.查看启动状态

分别在各个节点上执行如下命令,查看ZooKeeper服务的状态:

bin/zkServer.sh status

在centos01节点上查看服务状态,输出了以下信息:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: follower

在centos02服务器上查看服务状态,输出了以下信息:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: follower

在centos03服务器上查看服务状态,输出了以下信息:
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: leader

由此可见,本例中centos03服务器上的ZooKeeper服务为Leader,其余两个ZooKeeper服务为Follower。
如果在查看启动状态时输出以下信息,说明ZooKeeper集群启动不成功,出现错误。
Error contacting service. It is probably not running.
此时需要修改bin/zkEvn.sh文件中的以下内容,将错误信息输出到日志文件。

if [ "x${ZOO_LOG4J_PROP}" = "x" ]  
then  
    ZOO_LOG4J_PROP="INFO,CONSOLE"  
fi  

将上述内容中的CONSOLE改为ROLLINGFILE,修改后的内容如下:

if [ "x${ZOO_LOG4J_PROP}" = "x" ]  
then  
    ZOO_LOG4J_PROP="INFO,ROLLINGFILE"  
fi  

修改后,重新启动ZooKeeper集群,查看在ZooKeeper安装目录下生成的日志文件zookeeper.log,发现报以下错误:
java.net.NoRouteToHostException: 没有到主机的路由。
产生上述错误的原因是,系统没有关闭防火墙,导致ZooKeeper集群间连接不成功。因此需要关闭系统防火墙(为了防止出错,在最初的集群环境配置的时候可以直接将防火墙关闭),CentOS7关闭防火墙的命令如下:

systemctl stop firewalld.service
systemctl disable firewalld.service

关闭各节点的防火墙后,重新启动ZooKeeper,再一次查看启动状态,发现一切正常了。

7.测试客户端连接

在centos01节点上(其它节点也可以),进入ZooKeeper安装目录,执行以下命令,可以连接ZooKeeper服务器,连接成功后可以输入ZooKeeper的Shell命令进行操作与测试了。

[hadoop@centos01]$ bin/zkCli.sh -server centos01:2181

8.2 命令行操作

【操作目的】
ZooKeeper的命令行工具类似于Shell。当ZooKeeper服务启动以后,可以在其中一台运行ZooKeeper服务的服务器中输入以下命令(需要进入ZooKeeper安装目录),启动一个客户端,连接到ZooKeeper集群:
[hadoop@centos01]$ bin/zkCli.sh -server centos01:2181
连接成功后,系统会输出ZooKeeper的运行环境及配置信息,并在屏幕输出“Welcome to ZooKeeper”等欢迎信息。之后就可以使用ZooKeeper命令行工具了。
【操作步骤】
以下是ZooKeeper命令行工具的一些简单操作示例:
(1)使用ls命令,可以查看当前ZooKeeper中所包含的内容:

[zk: centos01:2181(CONNECTED) 4] ls /
[zookeeper]

可以看到,当前有一个名称为zookeeper的Znode节点。
(2)使用create命令,可以创建一个新的Znode节点。例如,创建一个名为“zk”的Znode以及在它上面存放的元数据字符串“myData”,命令及输出信息如下:

[zk: centos01:2181(CONNECTED) 2] create /zk "myData"
Created /zk 

(3)使用get命令,可以查看某个Znode的详细信息及其包含的元数据字符串。例如,查看Znode节点/zk的详细信息,命令及输出信息如下:

[zk: centos01:2181(CONNECTED) 6] get /zk
myData
cZxid = 0x800000002
ctime = Thu Mar 22 10:12:11 CST 2018
mZxid = 0x800000002
mtime = Thu Mar 22 10:12:11 CST 2018
pZxid = 0x800000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0

(4)使用set命令,可以修改Znode节点的元数据字符串。例如,将Znode节点/zk所关联的字符串修改为“myDataUpdate”,命令及输出信息如下:

[zk: centos01:2181(CONNECTED) 10] set /zk "myDataUpdate"
cZxid = 0x800000002
ctime = Thu Mar 22 10:12:11 CST 2018
mZxid = 0x800000005
mtime = Thu Mar 22 10:18:19 CST 2018
pZxid = 0x800000002
cversion = 0
dataVersion = 3
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 0

(5)使用delete命令,可以将某个Znode节点删除。例如,删除上面创建的Znode节点/zk,命令如下:

[zk: centos01:2181(CONNECTED) 11] delete /zk

使用ZooKeeper命令行工具也可以创建有层次的目录。例如,在/zk节点目录下创建新的目录node1,并关联其元数据为“nodeData”,命令及输出信息如下:

[zk: centos01:2181(CONNECTED) 18] create /zk/node1 "nodeData"
Created /zk/node1

8.3 Java API操作

除了可以使用命令行方式对ZooKeeper进行操作外,ZooKeeper还提供了Java API操作接口。下面对ZooKeeper的常用Java API接口进行介绍。

8.3.1 创建Java工程

在编写Java API之前,首先需要新建一个ZooKeeper项目。ZooKeeper项目的结构与普通的Java项目一样,只是依赖的jar包不同。
在eclipse中新建一个Maven项目zk_demo(Maven项目的搭建此处不做过多讲解),然后在该项目的pom.xml文件中添加以下代码,以引入ZooKeeper的Java API依赖包:

   <dependency>
	 <groupId>org.apache.zookeeper</groupId>
	 <artifactId>zookeeper</artifactId>
	 <version>3.4.9</version>
   </dependency>

配置好pom.xml后,即可进行ZooKeeper Java API的编写。

8.3.2 创建节点

Zookeeper创建节点不支持递归调用,即无法在父节点不存在的情况下创建一个子节点,如在/zk0l节点不存在的情况下创建/zk01/ch01节点;并且如果一个节点已经存在,那么创建同名节点时,会抛出NodeExistsException异常。
下面我们创建一个节点/ zk001,节点的元数据为“zk001_data”,步骤如下:

1.编写代码

在新建的zk_demo项目中新建Java类CreatePath.java,完整代码如下所示:

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;

public class CreatePath {

	public static void main(String[] args) throws Exception {
		String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181";
		//参数1:服务器连接字符串
		//参数2:连接超时时间
		//参数3:观察者对象(回调函数)
		ZooKeeper zk = new ZooKeeper(connectStr, 3000, null);		
		/* 1.CreateMode 取值   
	     *  PERSISTENT:持久化节点
	     *  PERSISTENT_SEQUENTIAL:顺序自动编号的目录节点
	     *  EPHEMERAL:临时目录节点,客户端断开连接时,这种节点会被自动删除 
	     *  EPHEMERAL_SEQUENTIAL:临时自动编号节点 
	     *  */
		String path=zk.create("/zk001", "zk001_data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
		System.out.println(path);
	}
}

2.程序解读

ZooKeeper zk  = new ZooKeeper(connectStr, 3000, null);

新建一个ZooKeeper对象,传入三个参数,第一个参数为以逗号分隔的服务器连接字符串,格式:“host:端口”,这里需要把所有的ZooKeeper服务器的地址都写上,而不是只写其中一台。ZooKeeper客户端对象将从连接串中挑选任意一个服务器进行连接,如果连接失败,将尝试连接另外一个服务器,直到建立连接。这样的好处是能保证ZooKeeper服务的高可靠性,防止因为其中一台机器宕机而导致连接失败。第二个参数为连接超时时间,这里是3秒。第三个参数为观察者对象,连接成功后会调用观察者对象中的回调函数,这里传入null即可。

String path=zk.create("/zk001", "zk001_data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

调用ZooKeeper对象的创建节点函数,返回创建的节点路径,并需要传入四个参数。第一个参数为节点名称。第二个参数为节点数据,需要转成字节数组。第三个参数为权限控制,这里使用ZooKeeper自带的完全开放权限Ids.OPEN_ACL_UNSAFE。第四个参数为创建模式,它是一个枚举类型,共有四个取值,PERSISTENT(持久化,这个目录节点存储的数据不会丢失 ,即客户端失去连接之后不会被自动删除)、PERSISTENT_SEQUENTIAL(顺序自动编号的目录节点,这种目录节点在命名上会根据当前已经存在的节点数自动加 1,然后将已经成功创建的目录节点名返回给客户端)、EPHEMERAL(临时目录节点,客户端断开连接时,这种节点会被自动删除)、EPHEMERAL_SEQUENTIAL(临时自动编号目录节点,客户端断开连接时,这种节点也会被自动删除)。

3.运行程序

直接在eclipse中右击运行该程序即可。

8.3.3 添加数据

我们可以通过调用ZooKeeper对象的setData()函数给节点添加数据,示例代码如下:

@Test  
    public void setNodeData() throws Exception {  
    	String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181";
    	ZooKeeper zk  = new ZooKeeper(connectStr, 3000, null);  
        Stat stat = zk.setData("/zk002", "zk002_data2".getBytes(), -1);  
        System.out.println(stat.getVersion());
    }

代码解析:

Stat stat = zk.setData("/zk002", "zk002_data2".getBytes(), -1);

setData函数的第一个参数为节点路径。第二个参数为需要添加的数据,并转成字节数组。第三个参数为版本号,-1代表所有版本。

8.3.4 获取数据

我们可以调用ZooKeeper对象的getData()函数,获得指定节点的数据,示例代码如下:

@Test  
    public void getNodeData() throws Exception {  
    	String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181";
    	ZooKeeper zk  = new ZooKeeper(connectStr, 3000, null);  
    	Stat stat=new Stat();
	//返回指定路径上的节点数据和节点状态,节点的状态会放入stat对象中
	byte[] bytes=zk.getData("/zk002", null, stat);
	System.out.println(new String(bytes));
    }

上述代码获取了节点/zk002的数据,而且转成了字符串进行了输出,并将节点/zk002的状态放入了对象stat中。如需查看状态信息,可以从对象stat中进行输出查看。
可以看到,在getData函数的第二个参数,传入的是null,也可以指定一个观察者对象watcher,对节点数据的变化进行监听,一旦有数据改变,就会触发watcher指定的回调函数。我们对上方代码加入观察者后,示例代码如下:

@Test  
    public void getNodeDataWatch() throws Exception {  
    	String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181";
    	ZooKeeper zk  = new ZooKeeper(connectStr, 3000, null);  
    	Stat stat=new Stat();
		//返回指定路径上的节点数据和节点状态,节点的状态会放入stat对象中
		byte[] bytes=zk.getData("/zk002", new Watcher(){
			@Override
			public void process(WatchedEvent event) {
				System.out.println(event.getType());
			}
			
		}, stat);
		System.out.println(new String(bytes));
		
		//改变节点数据,触发watch
		zk.setData("/zk002", "zk002_data_testwatch".getBytes(), -1); 
		
		//为了验证是否触发了watch,不让程序结束
		while(true){
			Thread.sleep(3000);
		}
		
    }

代码分析:

public void process(WatchedEvent event) {
	System.out.println(event.getType());
}

process 方法是 Watcher 接口中的一个回调方法,当 ZooKeeper 向客户端发送一个 Watcher 事件通知时,客户端就会对相应的 process 方法进行回调,从而实现对事件的处理。
process 方法包含 WatcherEvent 类型的参数,WatchedEvent 包含了每一个事件的三个基本属性:通知状态(KeeperState)、事件类型(EventType)和节点路径(Path),ZooKeeper 使用 WatchedEvent 对象来封装服务端事件并传递给 Watcher,从而方便回调方法 process 对服务端事件进行处理。
上述代码通过System.out.println(event.getType());输出服务端的事件类型,输出结果为NodeDataChanged。从结果单词的含义可知,节点数据被改变了。

while(true){
	Thread.sleep(3000);
}

为了能够更好的验证是否触发了watch,不让程序一次执行到底,从而加入了上方代码,让程序一直停留在此处。

8.3.5 删除节点

我们可以通过调用ZooKeeper对象的delete()函数,对指定路径节点进行删除。示例代码如下:

@Test
	public void deletePath() throws Exception{
		String connectStr="192.168.170.128:2181,192.168.170.129:2181,192.168.170.130:2181";
		ZooKeeper zk  = new ZooKeeper(connectStr, 3000, null);  
		//删除节点 
		zk.delete("/zk001", -1);
	}

上述代码中,delete函数需要传入两个参数,第一个参数为需要删除的节点路径。第二个参数为节点版本,如果是-1则代表删除所有版本。

原创文章,转载请注明出处!!

posted @ 2018-07-10 16:38  大数据实战派  阅读(457)  评论(0编辑  收藏  举报