ZooKeeper学习之内部原理

本文涉及的产品
云原生网关 MSE Higress,422元/月
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
数据可视化DataV,5个大屏 1个月
简介: ZooKeeper学习之内部原理

这里学习Zookeeper的选举机制、节点类型、Stat结构体以及写数据流程。

【1】选举机制


Zookeeper适合安装在奇数台服务器上,集群中半数以上的机器存活,则集群可用。故,又称之为半数机制。


Zookeeper虽然在配置文件中并没有指定Master和Slave,但是Zookeeper集群工作时是有一个节点为leader,其他为follower。leader是通过内部的选举机制临时产生的。


假设有五台服务器组成的Zookeeper集群,它们的id是1-5,同时它们都是最新启动的,也就是没有历史数据(在存放数据量这一点上,它们都是一样的)。如下图所示:

(1) 服务器1启动,此时只有它一台服务器启动了,它发出去的报文没有响应,所以它的选举机制一直是LOOKING状态(它投了自己一票)。


(2)服务器2启动,它与服务器1进行通信,互相交换自己的选举结果(服务器2投自己一票,服务器1投服务器2一票,此时服务器2两票,服务器1一票)。由于两者都没有历史数据,所以id值较大的服务器2胜出。但是由于没有达到超过半数以上故而leader未产生。


(3)服务器3启动,先投自己一票,然后与服务器1,2交换选举结果。由于服务器1 2均未产生leader,故而在服务器3启动时会投其一票。此时服务器3票数为3票,已经超过半数,故而晋级为leader。


(4)服务器4启动,此时已经有了leader,自动为follower(先入为主机制)。


(5)同服务器4一样,成功leader的小弟。


【2】Zookeeper的节点类型


节点类型根据持久性分为持久性节点和临时性节点(短暂存活)。

  • 持久性节点:客户端和服务端断开连接后,创建的节点不删除。
  • 临时性节点:客户端和服务端断开连接后,创建的节点自己删除。


① 持久化目录节点

客户端与Zookeeper断开连接后,该节点依旧存在。

② 持久化顺序编号目录节点


客户端与Zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号。


说明:创建znode时设置顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器,由父节点维护。


在分布式系统中,顺序号可以被用于为所有的事件进行全局排序。这样客户端可以根据顺序号推断事件的顺序。


③ 临时目录节点

客户端与Zookeeper端口连接后,该节点被删除。

④ 临时顺序编号目录节点

客户端与Zookeeper端口连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号。


【3】客户端命令行操作

客户端连接与关闭

连接命令如下所示(在bin目录下执行):

zkCli.cmd  -server 127.0.0.1:3182


连接成功如下图(状态会是CONNECTED):

可以使用命令close关闭连接:

再次使用connect命令进行连接,其会重新变为CONNECTED状态:

connect 127.0.0.1:3182

① 创建普通节点

//创建节点时必须同时包含数据
[zk: localhost:2181(CONNECTED) 9] create /jane1 love
Created /jane1
[zk: localhost:2181(CONNECTED) 10] ls /
[jane1, zookeeper]

若是集群下操作,将会同步到其他server:

② 创建短暂节点

[zk: localhost:2181(CONNECTED) 11] create -e /jane2 love
Created /jane2

当客户端端口连接后,该节点将不存在。如下当客户端端口连接后集群中其他Zookeeper监控情况:

[zk: localhost:2181(CONNECTED) 1] ls /
[jane1, jane2, zookeeper]
[zk: localhost:2181(CONNECTED) 2] ls /
[jane1, zookeeper]

③ 创建带序号的节点

[zk: localhost:2181(CONNECTED) 0] ls /
[jane1, zookeeper]
[zk: localhost:2181(CONNECTED) 1] create -s /jane2 loave
Created /jane20000000002
[zk: localhost:2181(CONNECTED) 4] create -s /jane1/jane11 love
Created /jane1/jane110000000000

如果路径下无节点,则序号从0开始。否则序号从节点数量开始。


④ 获取节点内容

[zk: localhost:2181(CONNECTED) 5] get /jane1
love

⑤ 修改节点数据

[zk: localhost:2181(CONNECTED) 8] set /jane1 "love me"
[zk: localhost:2181(CONNECTED) 9] get /jane1
love me

⑥ 集群环境下节点值变化监听

在server2中注册监听:

[zk: localhost:2181(CONNECTED) 4] get /jane1 watch
'get path [watch]' has been deprecated. Please use 'get [-s] [-w] path' instead.
love me
[zk: localhost:2181(CONNECTED) 5]
WATCHER::

在server1中修改/jane1的值同时观察server2变化:

⑦ 监听节点的子节点变化(路径变化)

在server2注册路径监听:

[zk: localhost:2181(CONNECTED) 5] ls  /jane1 watch
'ls path [watch]' has been deprecated. Please use 'ls [-w] path' instead.
[jan12, jane11, jane110000000000]
[zk: localhost:2181(CONNECTED) 6] ls -w /jane1
[jan12, jane11, jane110000000000]

server1中为/jane1节点创建子节点同时观察server2变化:

可以发现监听只有一次有效。


⑧ 删除节点

delete  /jane1/jane12

⑨ 递归删除节点

rmr  /jane1/jane13

⑩ 查看节点状态

[zk: localhost:2181(CONNECTED) 23] stat /jane1
cZxid = 0x100000002
ctime = Sun Oct 06 15:36:15 CST 2019
mZxid = 0x10000000d
mtime = Sun Oct 06 15:55:45 CST 2019
pZxid = 0x100000011
cversion = 6
dataVersion = 3
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 4

(11)ls 与ls2区别

二者打印路径信息分别如下:

//ls2命令是ls命令的增强版,比ls命令多输出本节点信息
[zk: 127.0.0.1:3182(CONNECTED) 3] ls2 /
'ls2' has been deprecated. Please use 'ls [-s] path' instead.
[zookeeper, vscrm_dev_domain]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x100000005
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 2
//ls命令用于获取路径下的节点信息,注意路径为绝对路径
[zk: 127.0.0.1:3182(CONNECTED) 4] ls /
[vscrm_dev_domain, zookeeper]

更多命令操作参考如下:

ZooKeeper -server host:port cmd args
        addauth scheme auth
        close
        config [-c] [-w] [-s]
        connect host:port
        create [-s] [-e] [-c] [-t ttl] path [data] [acl]
        delete [-v version] path
        deleteall path
        delquota [-n|-b] path
        get [-s] [-w] path
        getAcl [-s] path
        history
        listquota path
        ls [-s] [-w] [-R] path
        ls2 path [watch]
        printwatches on|off
        quit
        reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,...]*]] | [-add serverId=host:port1:port2;port3[,...]]* [-remove serverId[,...]*]
        redo cmdno
        removewatches path [-c|-d|-a] [-l]
        rmr path
        set [-s] [-v version] path data
        setAcl [-s] [-v version] [-R] path acl
        setquota -n|-b val path
        stat [-w] path
        sync path

【4】Stat结构体

节点结构如下所示:

[zk: localhost:2181(CONNECTED) 23] stat /jane1
cZxid = 0x100000002
ctime = Sun Oct 06 15:36:15 CST 2019
mZxid = 0x10000000d
mtime = Sun Oct 06 15:55:45 CST 2019
pZxid = 0x100000011
cversion = 6
dataVersion = 3
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 4

① cZxid


创建节点的事务zxid。每次修改Zookeeper状态都会收到一个zxid形式的时间戳,也就是Zookeeper事务ID。事务ID是Zookeeper中所有修改总的次序,每个修改都有唯一的zxid。如果zxid1小于zxid2,那么zxid1在zxid2之前发生。


② ctime

znode被创建的毫秒数(从1970年开始)。

③ mZxid

znode最后更新的事务zxid。


④ mtime


znode最后修改的毫秒数(从1970年开始)。


⑤ pZxid


znode最后更新的子节点zxid。


⑥ cversion


znode子节点变化号,znode子节点修改次数。


⑦ dataVersion


znode数据变化号。


⑧ aclVersion


znode访问控制列表的变化号。


⑨ ephemeralOwner


如果是临时节点,这个是znode拥有者的session id。如果不是临时节点,则是0。


⑩ dataLength


znode的数据长度。


(11) numChildren

znode子节点数量。


【5】Zookeeper监听器原理

① 监听器原理详解


首先要有一个main()线程;

在main线程创建Zookeeper客户端。这时就会创建两个线程,一个负责网络连接通信(connect),一个负责监听(listener)。

通过connect线程将注册的监听事件发送给Zookeeper。

将注册的监听事件添加到Zookeeper的注册监听器列表中。

Zookeeper监听到有数据或者路径变化,就会将这个消息发送给listener线程。

listener线程内部调用了process()方法(其实listener相当于回调函数)。


② 常见的监听器

  • 监听节点数据变化
get path [watch]
get [-s] [-w] path


  • 监听子节点增减变化
ls2   path   [watch]
ls    path   [watch]
ls    -w      path


【6】Java操作zookeeper

这里用的原生java,没有用框架,需要的jar 依赖如下:

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


① 获取zookeeper客户端

private String connectString="127.0.0.1:3181,127.0.0.1:3182,127.0.0.1:3183";
private int sessionTimeout = 2000;
private ZooKeeper zkClient;
@Before
public void init() throws IOException{
  //获取客户端对象并注册监听
  zkClient = new ZooKeeper(connectString, sessionTimeout , new Watcher() {
    public void process(WatchedEvent event) {
      System.out.println("---------start----------");
      List<String> children;
      try {
        children = zkClient.getChildren("/", true);
        for (String child : children) {
          System.out.println(child);
        }
        System.out.println("---------end----------");
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  });
}

② 创建节点

@Test
public void createNode() throws KeeperException, InterruptedException{
  String path = zkClient.create("/janus", "love".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
  System.out.println(path);
}

③ 获取子节点,并监控节点的变化

@Test
public void getDataAndWatch() throws KeeperException, InterruptedException{
  List<String> children = zkClient.getChildren("/", true);
  //保持监听线程一直运行
  Thread.sleep(Long.MAX_VALUE);
}

示意图如下:


④ 判断节点是否存在

@Test
public void exist() throws KeeperException, InterruptedException{
  Stat stat = zkClient.exists("/janus", false);
  System.out.println(stat==null? "not exist":"exist");
}

⑤ 删除节点

@Test
public void deleteNode() throws KeeperException, InterruptedException{
  zkClient.delete("/jane2",0);
}

注意,如果节点下有子孩子,则不许删除。

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
3月前
|
消息中间件 分布式计算 算法
深入理解Zookeeper系列-3.Zookeeper实现原理及Leader选举源码分析(上)
深入理解Zookeeper系列-3.Zookeeper实现原理及Leader选举源码分析
172 0
|
3月前
|
NoSQL 中间件 API
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)(下)
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)
115 2
|
3月前
|
NoSQL Java API
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)(上)
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)
110 0
|
3月前
|
网络协议 中间件 数据库
Zookeeper学习系列【三】Zookeeper 集群架构、读写机制以及一致性原理(ZAB协议)
Zookeeper学习系列【三】Zookeeper 集群架构、读写机制以及一致性原理(ZAB协议)
205 0
|
3月前
|
网络协议
Zookeeper学习系列【二】Zookeeper 集群章节之集群搭建
Zookeeper学习系列【二】Zookeeper 集群章节之集群搭建
72 0
|
3月前
|
存储 Dubbo 网络协议
Zookeeper学习系列【一】 教会你Zookeeper的一些基础概念
Zookeeper学习系列【一】 教会你Zookeeper的一些基础概念
61 0
|
3月前
|
存储 API
深入理解Zookeeper系列-4.Watcher原理
深入理解Zookeeper系列-4.Watcher原理
41 1
|
3月前
|
监控 算法 网络协议
深入理解Zookeeper系列-3.Zookeeper实现原理及Leader选举源码分析(下)
深入理解Zookeeper系列-3.Zookeeper实现原理及Leader选举源码分析
53 1
|
3月前
|
NoSQL Java API
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)
323 0
|
29天前
|
监控 NoSQL Java
分布式锁实现原理问题之ZooKeeper的观察器(Watcher)特点问题如何解决
分布式锁实现原理问题之ZooKeeper的观察器(Watcher)特点问题如何解决