avatar

目录
ZooKeeper学习笔记

ZooKeeper学习笔记

一、简介

1.1 概括

  1. 管理大量主机的协同服务。
  2. 分布式应用,实现分布式读写技术。
  3. zookeeper提供的服务
    • Naming service:按名称区分集群中的节点
    • Configuration management:对加入节点的最新化处理
    • Cluster management:实时感知集群中节点的增减
    • Leader election:leader选举
    • Locking and synchronization service:修改时锁定数据,实现容灾
    • Highly reliable data registry:节点宕机数据也是可用的

1.2 工作机制

Zookeeper从设计模式角度来看:是一个基于观察者模式的设计的分布式管理框架,它负责存储和管理大家大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,zookeeper就将负责通知已经在zookeeper注册的那些观察者做出相应的反应。

avatar

  1. 一个领导者(leader)多个跟随着(follower)组成的集群
  2. 集群中只要有半数以上节点存活,zookeeper集群就能正常服务
  3. 全局数据一致:每个server保存一份相同的数据副本,client无论连接到哪一个server,数据都是一致的
  4. 更新请求顺序进行,来自用一个client的更新请求按其发送的顺序依次执行
  5. 数据更新原子性,依次数据更新要么成功,要么失败
  6. 实时性在一定时间范围内,client能读取到最新消息(数据量少,主要放配置信息,同步速度快)

二、安装

2.1 单机版

  1. jdk安装

  2. 下载zookeeper-3.4.9.tar.gz

  3. tar开包,到指定目录下

    Code
    $tar -zxvf zookeeper-3.4.9.tar.gz
    $sudo mv zookeeper-3.4.9 /soft/zookeeper-3.4.9
  4. 创建链接

    Code
    $ln -s zookeeper-3.4.9 zookeeper
  5. 配置环境变量

    Code
    # ZooKeeper
    export ZK_HOME=/soft/zookeeper
    export PATH=$PATH:$ZK_HOME/bin

    # 更新资源
    $source /etc/profile
  6. 配置zoo.cfg

    Code
    [wbw@s201 /soft/zookeeper/conf]$cp zoo_sample.cfg zoo.cfg
    [wbw@s201 /soft/zookeeper/conf]$vi zoo.cfg
    # 修改数据目录,其余配置不用动
    dataDir=/home/wbw/zookeeper
  7. 自定义日志目录

    修改 /bin/zkEnv.sh文件

    Code
    [wbw@s201 /soft/zookeeper/bin]$vi zkEnv.sh
    # 修改以下两个地方的目录,将日志文件输出到安装目录
    if [ "x${ZOO_LOG_DIR}" = "x" ]
    then
    ZOO_LOG_DIR="${ZOOKEEPER_PREFIX}/logs"
    fi

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

    修改/conf/log4j.properties文件

    Code
    [wbw@s201 /soft/zookeeper/conf]$vi log4j.properties
    # 修改以下几个地方
    zookeeper.root.logger=INFO, ROLLINGFILE
    zookeeper.log.dir=/soft/zookeeper/logs
    zookeeper.tracelog.dir=/soft/zookeeper/logs
    log4j.appender.ROLLINGFILE=org.apache.log4j.DailyRollingFileAppender
    #log4j.appender.ROLLINGFILE.MaxFileSize=10MB (把这句话注释掉)
  8. 启动zk服务器

    Code
    $zkServer.sh start
  9. 验证zk

    Code
    $jps
    1606 QuorumPeerMain
    $netstat -anop | grep 2181

3.2 完全分布式

  1. 首先我们规划s201,s202,s203三台主机作为zookeeper节点

  2. 在每台机子上都安装zk,修改环境变量(和单机版一样,可以先做第3步,然后分发)

  3. 配置所有zk节点的配置文件zoo.cfg,末尾追加:

    xml
    # server.n=host:port1:port2,数字n必须是myid中的值
    # port1:leader端口, 作为leader时,供follower连接的端口
    # port2:选举端口,选举leader时,供其他follower连接的端口
    server.1=s201:2888:3888
    server.2=s202:2888:3888
    server.3=s203:2888:3888
  4. 在每台主机的ZK数据存放目录中添加myid

    Code
    [wbw@s201 /home/wbw/zookeeper]$echo 1 > myid
    [wbw@s202 /home/wbw/zookeeper]$echo 2 > myid
    [wbw@s203 /home/wbw/zookeeper]$echo 3 > myid
  5. 启动服务集群

    Code
    [wbw@s201 /home/wbw/zookeeper]$zkServer.sh start
    [wbw@s202 /home/wbw/zookeeper]$zkServer.sh start
    [wbw@s203 /home/wbw/zookeeper]$zkServer.sh start
  6. 查看每台服务器状态

    Code
    [wbw@s201 /home/wbw/zookeeper]$zkServer.sh status
    Mode: leader

    [wbw@s202 /home/wbw/zookeeper]$zkServer.sh status
    Mode: follower

    [wbw@s203 /home/wbw/zookeeper]$zkServer.sh status
    Mode: follower
  7. 测试选举

    Code
    [wbw@s201 /home/wbw/zookeeper]$zkServer.sh stop

    [wbw@s202 /home/wbw/zookeeper]$zkServer.sh status
    Mode: follower

    [wbw@s203 /home/wbw/zookeeper]$zkServer.sh status
    Mode: leader

三、访问zk

3.1 客户端

zkCli.sh 连接

Code
$zkCli.sh -server s201:2181

如果是连接本机,那么可以简写:

Code
$zkCli.sh
[zk: localhost:2181(CONNECTED) 0]

help 帮助

Code
help

quit 退出

Code
quit

create 创建节点

创建节点(zk中以目录形式存在,逻辑上的 ):一般放配置信息,大小一般<1M。

Code
# 就像MAP那样,KV对。
# 在/a下添加tom内容(/a就是一个节点,</a, tom>)
create /a tom

# 必须要有父节点,每次创建都必须要写数据,以下是不行的。
create /a/a1/a11 tom2
# 需要进行如下创建:
create /a tom
create /a/a1 tom1
create /a/a1/a11 tom2

ls 列表

Code
[zk: localhost:2181(CONNECTED) 1] ls /
[a, zookeeper]

get 查看节点

Code
[zk: localhost:2181(CONNECTED) 2] get /a
tom
cZxid = 0x6
ctime = Sun Mar 08 20:28:40 CST 2020
mZxid = 0x6
mtime = Sun Mar 08 20:28:40 CST 2020
pZxid = 0x6
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0

set 设置数据

Code
[zk: localhost:2181(CONNECTED) 3] set /a tom2
[zk: localhost:2181(CONNECTED) 4] get /a
tom2

delete 删除节点

Code
[zk: localhost:2181(CONNECTED) 5] delete /a
[zk: localhost:2181(CONNECTED) 6] ls /
[zookeeper]

rmr 递归删除节点

Code
[zk: localhost:2181(CONNECTED) 7] create /a tom
Created /a
[zk: localhost:2181(CONNECTED) 8] create /a/a1 tom1
Created /a/a1
[zk: localhost:2181(CONNECTED) 9] create /a/a1/a11 tom2
Created /a/a1/a11
[zk: localhost:2181(CONNECTED) 11] ls /a/a1
[a11]
[zk: localhost:2181(CONNECTED) 12] rmr /a
[zk: localhost:2181(CONNECTED) 13] ls /
[zookeeper]

3.2 通过API

  1. 导入依赖

    xml
    <dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.9</version>
    </dependency>
  2. 编写代码

    java
    /**
    * 显示指定路径下的节点
    */
    @Test
    public void testLs() throws IOException, KeeperException, InterruptedException {
    ZooKeeper zk = new ZooKeeper("192.168.174.201:2181", 5000, null);
    List<String> children = zk.getChildren("/a", null);
    for (String child : children) {
    System.out.println(child);
    }
    }

    /**
    * 递归显示节点
    */
    @Test
    public void testLsR() throws IOException, KeeperException, InterruptedException {
    ZooKeeper zk = new ZooKeeper("192.168.174.201:2181", 5000, null);
    lsAll(zk,"/");
    }

    private void lsAll(ZooKeeper zk, String path) throws KeeperException, InterruptedException {
    System.out.println(path);
    List<String> children = zk.getChildren(path, null);
    if (null != children && !children.isEmpty()) {
    for (String child : children) {
    if (path.equals("/")) {
    lsAll(zk, path + child);
    } else {
    lsAll(zk, path + "/" + child);
    }
    }
    }
    }

    /**
    * 设置数据、查看数据
    * NOTE:每次更新数据,版本号都会+1,如果有2个人同时修改数据,那么版本号一旦有问题,将修改失败
    */
    @Test
    public void testSet() throws IOException, KeeperException, InterruptedException {
    ZooKeeper zk = new ZooKeeper("192.168.174.201:2181", 5000, null);
    System.out.println(new String(zk.getData("/a", false, null)));
    // 这里的版本号要特别注意,如果错误了。设不成功!!!!
    zk.setData("/a", "tomtom".getBytes(), 0);
    System.out.println(new String(zk.getData("/a", false, null)));
    }

四、zk架构

4.1 Client

从server获取信息,周期性发送数据给server,表示自己还活着。

client连接时,server回传ack信息。

如果client没有收到reponse,自动重定向到另一个server。

4.2 Server

zk集群中的一员,向client提供所有service,回传ack信息给client,表示自己还活着。

4.3 Ensemble

一组服务器,最小节点数是3。

最小节点数是3。

4.4 Leader

如果连接的节点失败,自定恢复,zk服务启动时,完成leader选举。

4.5 Follower

追寻leader指令的节点。

五、节点

5.1 znode

zk中的节点,维护了stat,由Version number, Action control list (ACL), Timestamp,Data length.构成。

每一个ZNode默认能够存储1MB数据,每个ZNode都可以通过其路径唯一标识(zookeeper数据模型结构和Unix文件系统类似)

5.2 节点类型

  1. 持久节点:client结束,还存在。使用create创建的就是。

  2. 临时节点:在client活动时有效,断开自动删除。临时节点不能有子节点。leader推选时使用。

    Code
    [zk: localhost:2181(CONNECTED) 26] create -e /c haha
    Created /c
    [zk: localhost:2181(CONNECTED) 27] ls /
    [b0000000003, a, c, zookeeper]
    [zk: localhost:2181(CONNECTED) 28] quit
    $zkCli.sh
    [zk: localhost:2181(CONNECTED) 1] ls /
    [b0000000003, a, zookeeper]
    java
    /**
    * 创建临时节点
    */
    @Test
    public void testEphemeral() throws IOException, KeeperException, InterruptedException {
    ZooKeeper zk = new ZooKeeper("192.168.174.201:2181", 5000, null);
    zk.create("/c", "haha".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
    // 断点!
    System.out.println("Debug");
    }
  1. (持久/临时)序列节点:在节点名之后附加10个数字,主要用于同步和锁。

    Code
    [zk: localhost:2181(CONNECTED) 21] create -s /b heihei
    Created /b0000000003

六、Session 会话

Session中的请求以FIFO执行,一旦client连接到server,session就建立了。sessionid分配client。

client以固定间隔向server发送心跳,表示session是valid的,zk集群如果在超时时候,没有收到心跳,判定为client挂了,与此同时,临时节点被删除。

注意:网络连接断开和Session过期是两种机制。网络连接断开,客户端会尝试到集群重连,但是Seesion并不会消失。只有重连时间超过Seesion的超时时间才会被清理。

七、Watches 观察者

7.1 监听器原理

avatar

  1. 在main线程中创建ZK客户端,然后创建两个线程,一个负责通信,一个负责监听。
  2. 通过connect线程将注册的监听事件发送给zk
  3. 在zk中,将监听事件添加到注册监听器列表中
  4. 当监听到数据或路径变化的适合,将消息发送到listener线程
  5. listener线程内部调用了process方法

7.2 JAVA API

client能够通过watch机制在数据发生变化时收到通知。

client可以在read 节点时设置观察者。watch机制会发送通知给注册的客户端。

观察模式只触发一次(watcher时间是一次性的触发器)

session过期,watch机制删除了。

java
/**
* 观察者
*/
@Test
public void testWatch() throws Exception{
// ZK集群地址,会话超时时间,监听器
final ZooKeeper zk = new ZooKeeper("192.168.174.201:2181", 5000, null);

Stat st = new Stat();

Watcher w = null ;
w = new Watcher() {
//回调
public void process(WatchedEvent event) {
try {
System.out.println("数据改了:" + new String(zk.getData("/a", this, null)));
} catch (Exception e) {
e.printStackTrace();
}
}
};

byte[] data = zk.getData("/a", w , st);
System.out.println(new String(data));

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

八、zk工作流程

  1. zk集群启动后,client连接到其中的一个节点,这个节点可以leader,也可以follower。

  2. 连通后,node分配一个id给client,发送ack信息给client。

  3. 如果客户端没有收到ack,连接到另一个节点。

  4. client周期性发送心跳信息给节点保证连接不会丢失。

  5. 如果client读取数据(读流程),发送请求给node,node读取自己数据库,返回节点数据给client。

  6. 如果client存储数据(写流程),将路径和数据发送给server,server转发给leader。

  7. leader再补发请求给所有follower。只有大多数(超过半数)节点成功响应,则写操作成功。

九、leader推选

9.1 选举机制

  1. 半数机制:集群中半数以上机器存活,集群可用。所以zookeeper适合安装在奇数台服务器。

9.2 选举流程

  1. 所有节点在同一目录下创建临时序列节点。
  2. 节点下会生成/xxx/xx000000001等节点。
  3. 序号最小的节点就是leader,其余就是follower.
  4. 每个节点观察小于自己节点的主机。(注册观察者)
  5. 如果leader挂了,对应znode删除了。
  6. 观察者收到通知。

十、使用场景

在HBase中主要是:

  • Master高可用管理
  • RegionServer宕机异常检测
  • 分布式锁

其他的什么负载均衡、服务器上下线等等。

文章作者: IT小王
文章链接: https://wangbowen.cn/2020/03/07/ZooKeeper%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 IT小王

评论