Zookeeper - Must Know

171 阅读6分钟

What's ZK

ZooKeeper是一个分布式服务协调框架,属于CAP原则中的CP系统(最终一致性+分区容错性)

How can u do with ZK

它主要是用来解决分布式一些数据管理问题:

  1. service discovery
  2. state syncronization
  3. decentralized config
  4. distributed lock
  5. ...

Priciple

Consistency

zk的是通过基于Paxos的ZAB协议(Zookeeper Atomic Broadcast)来实现一致性的, 但默认只是最终一致性

原因是: 不同于Raft算法的读写都只能在Leader操作, ZAB协议下ZK的只能在Leader操作但是ZK的可以在Follower上进行, 所以ZK满足如下case:

  1. Leader保证一个client的顺序写, 即写操作的顺序在所有 Follower 上是保持一致的, 比如一个client要求先完成写操作 A、接着写操作 B、之后是写操作 C,那么在最终整体的写请求中将看到是先写 A 再写 B 再写 C 的,但 A B C 可能不是相邻的(因为可能插入其他client的写请求)
  2. client A 更新了键 X 的值,而 client B 在另一台服务器上读取键 X 的值可能会读到更新之前的值 (这里能看出来不是线性一致的), 因为Leader还没把数据同步到Slave
  3. 同一个client顺序向两个服务器A和B分别发起写和读请求, 因为请求会带上zxid标记, 在服务器B读的时候如果发现服务器B最新的zxid小于自身的zxid则请求会阻塞; 所以实际上zk对于单个客户端线性一致的; 所以zk提供了sync命令, sync命令本质上是一个写+读的组合命令, 得益于zxid所以能可以通过这种方式保证读到的是最新的

ZK的节点类型

  1. PERSISTENT : create /first_znode
  2. EPHEMERAL : create -e /tmp_znode_1
  3. PERSISTENT_SEQUENTIAL : create -s /first_znode
  4. EPHEMERAL_SEQUENTIAL : create -e -s /tmp_znode_1

ephemeral node can't have child node

ZK的监控机制及应用

cli命令 : get -w /normal_znode0000000007

通过监听机制能实现很多功能:

  1. 监听集群中各节点的状态(配合临时节点)
  2. 节点存储的是配置, 各节点可以监听配置的更新
  3. 服务发现, 节点以PSM命名, 节点的值是IP列表
  4. 实现分布式锁(配合临时节点或顺序节点) : 比如排他锁取锁可以在/lock节点下create -e, 谁create成功了谁就取到锁, 取锁失败的监听这个临时节点, 等解锁收到通知后再create -e; 再比如可以使用顺序节点create -e -s创建完成后再调用ls命令查看自己创建的顺序节点是不是排在第一个,如果不是则监听前一个顺序临时节点

ZK写数据流程

  1. 如果client连接的是follower, follower会先将命令发到leader (类似于etcdforward)
  2. leader会将写命令广播到所有follower (以提案的形式proposal)
  3. 在超过半数follower给leader正确的返回后, leader会给所有slave发送commit消息
  4. 所有follower会将上一个提案提交 (类似于二阶段提交)

write to leader image.png

write to follower

image.png

Data Structure

ZNode

zk是树状存储, 每个存储节点叫znode, 最大可存储1MB

ZXid

zxid is noly generated by leader, the structure of zxid is that:

image.png

leader 每发起一个提案就会同时生成一个zxid, zxid是顺序递增生成

分布式

zk server has three roles:

  1. leader : 同一时间只会有一个实际工作的 Leader,它会发起并维护与各 Follwer 及 Observer 间的心跳。所有的操作必须要通过 Leader完成再由 Leader 将写操作广播给其它服务器
  2. follower : 可同时存在多个 Follower,它会响应 Leader 的心跳。Follower 可直接处理并返回客户端的请求,同时会将请求转发给 Leader 处理,参与事务请求 Proposal 的投票及 Leader 选举投票
  3. observer : 可同时存在多个 Observer, 功能与 Follower 类似,但是,不参与投票 (为了性能考虑, 当follower节点过多时, 投票很耗时间)

ZK的使用

Distributed Lock

ZK作为分布式锁性能优于etcd

redisetcdzookeeper
优点并发高RAFT协议使用临时节点可以达到公平锁; 已经被锁时可注册回调事件(no need poll)
缺点取锁需要重试(耗CPU);一主多从可能存在多个client取到锁情况延时比较高, 已经被锁时可需要业务方重试拿到锁的节点断网情况下,后一个节点也会拿到锁

Bad Cases:

现在clientA已经拿到锁了,它的临时节点排在第一位, 有一个clientB临时节点排在第二位, 同时watch了第一个节点, 如果此时clientA的连接断了, clientB就会认为锁被释放了, 所以它也会拿到锁

ZK的使用

搭建启动单机伪集群

cloud.tencent.com/developer/a…

依次启动zoo3.cfg zoo2.cfg zoo1.cfg

-   # 依次启动zoo3.cfg zoo2.cfg zoo1.cfg
-   zookeeper-bin ❯ bin/zkserver.sh start conf/zoo3.cfg
-   /usr/bin/java
-   ZooKeeper JMX enabled by default
-   Using config: conf/zoo3.cfg
-   Starting zookeeper ... STARTED

查看节点状态

-   # 查看节点状态
-   zookeeper-bin ❯ bin/zkserver.sh status conf/zoo3.cfg
-   /usr/bin/java
-   ZooKeeper JMX enabled by default
-   Using config: conf/zoo3.cfg
-   Client port found: 2183. Client address: localhost. Client SSL: false.
-   Mode: leader

操作节点

用cli命令连接node - zookeeper-bin ❯ bin/zkcli.sh -server 127.0.0.1:2183

创建节点

# 创建节点
[zk: 127.0.0.1:2181(CONNECTED) 1] create /first_znode
Created /first_znode

# 查看节点下的所有节点
[zk: 127.0.0.1:2183(CONNECTED) 2] ls /
[first_znode, t1, zookeeper]

设置节点的值

# 设置节点的值
[zk: 127.0.0.1:2183(CONNECTED) 3] set /first_znode "set from 3"

# 读节点的值
[zk: 127.0.0.1:2181(CONNECTED) 4] get /first_znode
set from 3

查看节点状态

# 查看节点状态
[zk: 127.0.0.1:2181(CONNECTED) 5] stat /first_znode 
cZxid = 0x400000003
ctime = Wed Aug 17 15:43:53 CST 2022
mZxid = 0x400000004
mtime = Wed Aug 17 15:51:13 CST 2022
pZxid = 0x400000003
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0

# 删除节点
[zk: 127.0.0.1:2181(CONNECTED) 6] delete /first_znode

创建临时节点

# 创建临时节点, 退出会话后该会话创建的临时节点会销毁
[zk: 127.0.0.1:2181(CONNECTED) 7] create -e /tmp_znode_1
Created /tmp_znode_1

创建顺序节点

# 创建顺序节点
[zk: 127.0.0.1:2183(CONNECTED) 3] create -s /normal_znode
Created /normal_znode0000000006
[zk: 127.0.0.1:2183(CONNECTED) 4] create -s /normal_znode
Created /normal_znode0000000007

退出会话

# 退出会话, 该会话创建的临时节点会销毁
[zk: 127.0.0.1:2183(CONNECTED) 7] quit

监听节点

# 监听节点
[zk: 127.0.0.1:2181(CONNECTED) 3] get -w /normal_znode0000000007
007

# 如果另一个cli更改了这个节点的值, 该cli会收到通知
# 但watcher是一次性的, 收到通知完后需要重新设置watcher
[zk: 127.0.0.1:2181(CONNECTED) 4] 
WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/normal_znode0000000007

Ref

  1. zhuanlan.zhihu.com/p/363396366
  2. tech.byte dance.net/articles/6895924688841605134
  3. cloud.tencent.com/developer/a…