欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

第8章 ZooKeeper操作

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

目录

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则代表删除所有版本。

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