统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等服务。ZooKeeper支持Java和C两种编程语言的接口,可以很方便地实现一致性、组管理、leader选举和某些协议。

一、ZooKeeper简介

1、一致性

顺序一致性:客户端的更新顺序与他们被发送的顺序相一致;

原子性:更新操作要么全部成功,要么全部失败;

单系统镜像:无论客户端连接到哪一个服务器,都可以看到相同的ZooKeeper视图;

可靠性:一旦一个更新操作被应用,那么在客户端再次更新它之前,其值将不会被改变;

实时性:在特定的一段时间内,系统的任何变更都将被客户端检测到;

2、为什么使用ZooKeeper

   大部分分布式应用需要一个主控、协调器或控制器来管理物理分布的子进程(如资源、任务分配等),而目前大部分应用需要开发私有的协调程序,缺乏一个通用的机制。此外协调程序的反复编写浪费,且难以形成通用、伸缩性好的协调器,由此诞生了一个能提供通用的分布式锁服务,用以协调分布式应用系统的协调服务ZooKeeper。

3、Zookeeper节点类型

emphemeral(短暂的)和persistent(持久的),并且节点类型在创建时制定后就不可更改。

emphmeral节点在客户端会话结束时就自动删除,短暂Znode下不可以有子节点。

persistent节点不依赖于客户端会话,只有当客户端明确执行删除操作时,该节点才会被删除。

4、ZooKeeper的数据模型

   在ZooKeeper中存在着节点的概念,通常称为Znode。Znode既含有数据。又可以用来标识路径,因此既可以被看作是一个文件,又可以被看作是一个目录。Znode的目录结构如下图所示:

zookeeper的版本如何在服务器上查看 zookeeper 查看服务_客户端

图1.1 ZooKeeper文件结构  

   说明:

   1) 每个Znode创建时会被自动编号,并且其有一个唯一的路径标识,如/SERVER2节点的标识就为/APP3/SERVER2;

   2) Znode与操作系统的文件不同,Znode可以拥有子Znode,并且可以存数据;

   3) Znode中的数据可以有多个版本,比如某一个路径下存有多个数据版本,那么查询这个路径下的数据就需要带上版本;

长连接方式,每个客户端和服务器通过心跳来保持连接,这个连接状态称为session,如果znode是临时节点,这个session失效,znode也就删除了;

   5) Znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的集中管理,集群管理,分布式锁等等;

5、ZooKeeper的角色

领导者(leader),负责进行投票的发起和决议,更新系统状态;

学习者(learner),包括跟随者(follower)和观察者(observer),follower用于接受客户端请求并向客户端返回结果,在选主过程中参与投票;

客户端(client),请求发起方;

   观察者observer可以接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度。

6、Zookeeper配置

   Zookeeper配置文件在conf文件夹中,几个重要配置参数说明:

tickTime:是Zookeeper服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个tickTime时间就会发送一个心跳,默认值2000(毫秒);

initLimit:这个配置项是用来配置Zookeeper接受Foller客户端初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过 10 个心跳的时间(也就是tickTime)长度后Zookeeper服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。默认值为10,也总的时间长度就是10*2000=20秒;

syncLimit:这个配置项标识Leader与Follower之间发送消息,请求和应答时间长度,最长不能超过多少个tickTime的时间长度。默认值为5,也就是5*2000=10秒;

dataDir:是Zookeeper保存数据的目录地址,默认情况下,Zookeeper的写数据的日志文件也保存在这个目录中;

clientPort:指客户端连接Zookeeper服务器的端口,Zookeeper会监听这个端口,接受客户端的访问请求;

server.A=B:C:D:A是一个数字,表示第几号服务器;B为IP或主机名;C表示的是这个服务器与集群中的Leader服务器交换信息的端口;D表示的是万一集群中的Leader服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口;

这里的“客户端”指的不是用户连接Zookeeper服务器的客户端,而是Zookeeper服务器集群中连接到Leader的Follower服务器;

二、ZooKeeper集群模式安装

集群模式下安装ZooKeeper,至少需要三个节点,并且最好使用奇数台的机器。如果想要集群能够忍受m台机器的故障,那么整个集群至少需要2m+1台机器。

   本实例安装版本:zookeeper-3.4.5.tar.gz,集群共有三个节点:hadoop0、hadoop1和hadoop2。详细安装步骤如下:

1、解压缩zookeerper-3.4.5.tar.gz,并将文件移动至/usr/local下重命名为zk,执行命令:①tar -zxvf  zookeerper-3.4.5.tar.gz ②mv zookeeper-3.4.5 /usr/local/zk

2、配置环境变量,执行命令:vi /etc/profile,增加一行:export ZOOKEEPER_HOME=/usr/local/zk,并在环境变量export PATH=.:.....:$PATH中增加:$ZOOKEEPER_HOME/bin,然后执行命令:source /etc/profile使配置文件立即生效

3、进入conf目录,执行命令:mv zoo_sample.cfg zoo.cfg,然后编辑此配置文件,执行命令:vi zoo.cfg

dataDir=/usr/local/zk/data

server.0=hadoop0:2888:3888

      server.1=hadoop1:2888:3888

      server.2=hadoop2:2888:3888

4、创建数据文件夹,执行命令:mkdir /usr/local/zk/data,并在data目录下创建文件myid,值为0

5、通过scp命令将zk安装目录复制到hadoop1和hadoop2中,同时将hadoop0的配置文件/etc/profile复制到其他节点中,执行命令(只例举hadoop1):

scp -r /usr/local/zk/ hadoop1:/usr/local/ 

scp /etc/profile hadoop1:/etc/ 

source /etc/profile

6、把hadoop1和hadoop2中相应的myid文件内容分别改为1和2,分别与配置文件zoo.cfg中的server.x对应

7、启动,在三个节点上分别执行命令:zkServer.sh start。启动后,检验当前节点的身份可执行命令:zkServer.sh status。进入客户端命令:zkCli.sh。

三、使用Java操作Zookeeper

   在Zookeeper客户端Cli.sh中可以执行的操作,在Java中都可以通过调用Zookeeper的API来完成。执行程序需要通过Zookeeper的2181端口来进行连接,因此建议测试环境下将防火墙关闭。

   操作示例代码如下(需导入zookeeper-x.x.x.jar及lib下的相关jar包,或导入Zookeeper的maven资源):

zookeeper的版本如何在服务器上查看 zookeeper 查看服务_hadoop_02

zookeeper的版本如何在服务器上查看 zookeeper 查看服务_hadoop_03

package com.hicoor.zookeeper;

import java.util.List;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;

public class ZookeeperDemo {

    private static String connectString = "172.19.7.31:2181";
    private static int sessionTimeout = 999999;
    private static String testNode = "/hans";
    
    /**
     * 为确保程序顺利运行,运行前最好关闭服务端的防火墙
     */
    public static void main(String[] args) throws Exception {
        //监视器
        Watcher watcher = new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("触发事件:" + event);
            }
        };
        //创建连接实例
        ZooKeeper zooKeeper = new ZooKeeper(connectString, sessionTimeout, watcher);
        System.out.println("获取连接:" + zooKeeper);
        
        //创建节点并设置值
        zooKeeper.create(testNode, "haha.mx".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        
        //重新设置节点值(不存在报异常)
        zooKeeper.setData(testNode, "hicoor.com".getBytes(), -1);
        
        //读取节点值
        getNodeData(watcher, zooKeeper);
        
        //删除节点
        zooKeeper.delete(testNode, -1);
        System.out.println("删除节点");
        
        List<String> children = zooKeeper.getChildren("/", watcher);
        System.out.println(children);
        
        //关闭连接实例
        zooKeeper.close();
    }

    //读取节点值
    private static void getNodeData(Watcher watcher, ZooKeeper zooKeeper)
            throws KeeperException, InterruptedException {
        
        byte[] data = zooKeeper.getData(testNode, watcher, null);
        System.out.println("读取的值:"+new String(data));
    }

}

View Code