如何理解Zookeeper集群的搭建与运作原理?

2026-05-22 18:071阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2540个文字,预计阅读时间需要11分钟。

1. 概述

1.1 简介

ZooKeeper 是 Apache 顶级项目之一,为分布式应用提供高效、高可用的分布式协调服务。它支持诸如数据发布/订阅、负载均衡、命名服务、分布式协调/锁以及通知和分布式锁等功能。

1 概述 1.1 简介

ZooKeeper 是 Apache 的一个顶级项目,为分布式应用提供高效、高可用的分布式协调服务,提供了诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知和分布式锁等分布式基础服务。由于 ZooKeeper 便捷的使用方式、卓越的性能(基于内存)和良好的稳定性,被广泛地应用于诸如 Hadoop、HBase、Kafka 和 Dubbo 等大型分布式系统中。
官方地址:zookeeper.apache.org

1.2 角色

领导者(leader):负责进行投票的发起和决议,更新系统状态。
跟随者(follower):用于接收客户端请求并给客户端返回结果,在选主过程中进行投票。
观察者(observer):可以接受客户端连接,将写请求转发给 leader,但是observer 不参加投票的过程,只是为了扩展系统,提高读取的速度。

1.3 节点特性

ZooKeeper 节点的生命周期取决于节点的类型。在 ZooKeeper 中,节点根据持续时间可以分为持久节点(PERSISTENT)临时节点(EPHEMERAL),根据是否有序可以分为顺序节点(SEQUENTIAL)、和无序节点(默认是无序的)。每个客户端连接zookeeper会产生一个session,客户端连接关闭时session也会消失
持久节点一旦被创建,除非主动移除,不然一直会保存在 Zookeeper 中(不会因为创建该节点的客户端的会话失效而消失)。

1.4 数据模型

层次化的目录结构,命名符合常规文件系统规范,类似于文件目录。
每个节点在 Zookeeper 中叫做 Znode,并且其有一个唯一的路径标识。
每个节点中数据存储不能超过1M(不要把Zookeeper当数据库用)。

1.5 特性

顺序一致性。客户端的更新将按发送顺序应用。
原子性。要么成功要失败。没有中间状态(最终一致性)。
单个系统映像。由于zookeeper使用复制集群,无论客户端连接哪个节点都能看到相同的数据。
可靠性。即所有节点支持持久化。
及时性。存储在节点中的数据会在较短时间内及时同步。

1.6 CAP

任何分布式架构都不能同时满足C(一致性)、A(可用性)、P(分区容错性)。zookeeper集群在保证一致性的同时,在A和P之间做了取舍,最终选择了P,因此可用性差一点。

2 zookeeper集群搭建 2.1 网络拓扑

2.2 环境准备

CentOS7 * 4
JDK8
Zookeeper3.6.3

2.3 安装

在q101服务器中配置好后发送到其他服务器。q101操作如下:

wget mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.6.3/apache-zookeeper-3.6.3-bin.tar.gz --no-check-certificate tar xf apache-zookeeper-3.6.3-bin.tar.gz mv apache-zookeeper-3.6.3-bin /usr/local/zookeepe cd /usr/local/zookeeper/conf mv zoo_sample.cfg zoo.cfg vi zoo.cfg # 1. 修改dataDir路径为“/var/zookeeper” # 2. 增加集群节点信息,信息如下 server.4=192.168.88.101:2888:3888 server.3=192.168.88.102:2888:3888 server.2=192.168.88.103:2888:3888 server.1=192.168.88.104:2888:3888 # 3. 保存退出 # 创建配置文件中设置的持久化目录 mkdir /var/zookeeper # 将节点信息中的节点ID写如myid中 echo 4 > /var/zookeeper/myid # 修改环境变量 增加zookeeper信息 # export ZOOKEEPER_HOME=/usr/local/zookeeper # 在export PATH末尾追加 :$ZOOKEEPER_HOME/bin vi /etc/profile source /etc/profile

详细配置说明

# leader和follower心跳时间,用于维护节点是否存活。单位毫秒 tickTime=2000 # 初始延迟。当follower追随leader时,leader允许初始延迟时间。 # 用tickTime乘以initLimit,此处为2000*10即20秒 initLimit=10 # 数据同步时间,及超过时间数据同步失败。 # 用tickTime乘以syncLimit,此处为2000*5即10秒 syncLimit=5 # 持久化目录,不建议存在tmp下 dataDir=/var/zookeeper # 客户端连接zookeeper端口号 clientPort=2181 # 允许客户端连接最大连接数 #maxClientCnxns=60 # dataDir中保留快照数量 #autopurge.snapRetainCount=3 # 清除任务时间间隔,单位小时。0表示禁用自动清楚功能 #autopurge.purgeInterval=1 # 自定义监控 #metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider #metricsProvider.baike.baidu.com/item/Paxos算法

4.2 ZAB协议

ZAB(ZooKeeper Atomic broadcast)即ZooKeeper原子消息广播协议,类似于一个二阶段提交过程(2PC)属于最终一致性。是zookeeper基于Paxos算法的简化实现。

所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为 Leader服务器,而余下的其他服务器则成为 Follower服务器。 Leader服务器负责将一个客户端事务请求转换成一个事务 Proposal(提议),并将该 Proposal分发給集群中所有的Follower服务器。之后 Leader服务器需要等待所有 Follower服务器的反馈,一旦超过半数的 Follower服务器进行了正确的反馈后,那么 Leader就会再次向所有的 Follower服务器分发 Commit消息,要求其将前一个 Proposal进行提交。

上图说明:
1:Client向Follower发出写操作;
2:Follower将收到的写请求转发给Leader处理;
3:Leader收到请求后分配一个全局单调递增的唯一的事务ID(即ZXID,按其先后顺序来进行排序与处理);
4.1:Leader服务器会为每一个 Follower服务器都各自分配一个单独的队列,然后将需要广播的事务Proposal依次放入这些队列中去,并且根据FIFO策略进行消息发送;每一个 Follower服务器在接收到这个事务 Proposal之后,都会首先将其以事务日志的形式写入到本地磁盘中去,并且在成功写入后反馈给 Leader服务器个Ack响应。Leader自己也会将事务日志写入磁盘;
4.2:当 Leader服务器接收到超过半数Follower的Ack响应后,就会广播一个Commit消息给所有的 Follower服务器以通知其进行事务提交(写入内存),同时 Leader自身也会完成对事务的提交。
5:由Leader将结果返回给Client1

上图中4.1和4.2步骤,由于Leader自身也计一票,如果Follower2 由于网络等原因没有给Leader Ack响应,但Leader和Follower 1 两票已超过半数,所以结果依然会成功。此时Client2请求Follower2获取“/aaa”数据时,可以通过sync 让Follower 2向Leader同步后再返回数据。

4.3 选举机制 4.3.1 节点网络

基于上面的第2章集群搭建,查看每台节点网络连接情况

yum install net-tools netstat -natp | egrep '(2888|3888)'

查看结果如下:

# q101信息 [root@q101 ~]# netstat -natp | egrep '(2888|3888)' tcp6 0 0 192.168.88.101:3888 :::* LISTEN 1924/java tcp6 0 0 192.168.88.101:58052 192.168.88.102:3888 ESTABLISHED 1924/java tcp6 0 0 192.168.88.101:39738 192.168.88.103:3888 ESTABLISHED 1924/java tcp6 0 0 192.168.88.101:54174 192.168.88.102:2888 ESTABLISHED 1924/java tcp6 0 0 192.168.88.101:47726 192.168.88.104:3888 ESTABLISHED 1924/java # q102信息 [root@q102 ~]# netstat -natp | egrep '(2888|3888)' tcp6 0 0 192.168.88.102:2888 :::* LISTEN 1661/java tcp6 0 0 192.168.88.102:3888 :::* LISTEN 1661/java tcp6 0 0 192.168.88.102:3888 192.168.88.101:58052 ESTABLISHED 1661/java tcp6 0 0 192.168.88.102:2888 192.168.88.103:38244 ESTABLISHED 1661/java tcp6 0 0 192.168.88.102:50104 192.168.88.104:3888 ESTABLISHED 1661/java tcp6 0 0 192.168.88.102:2888 192.168.88.104:48034 ESTABLISHED 1661/java tcp6 0 0 192.168.88.102:2888 192.168.88.101:54174 ESTABLISHED 1661/java tcp6 0 0 192.168.88.102:37466 192.168.88.103:3888 ESTABLISHED 1661/java # q103信息 [root@q103 ~]# netstat -natp | egrep '(2888|3888)' tcp6 0 0 192.168.88.103:3888 :::* LISTEN 1415/java tcp6 0 0 192.168.88.103:38244 192.168.88.102:2888 ESTABLISHED 1415/java tcp6 0 0 192.168.88.103:3888 192.168.88.101:39738 ESTABLISHED 1415/java tcp6 0 0 192.168.88.103:3888 192.168.88.102:37466 ESTABLISHED 1415/java tcp6 0 0 192.168.88.103:58344 192.168.88.104:3888 ESTABLISHED 1415/java # q104信息 [root@q104 ~]# netstat -natp | egrep '(2888|3888)' tcp6 0 0 192.168.88.104:3888 :::* LISTEN 1333/java tcp6 0 0 192.168.88.104:3888 192.168.88.101:47726 ESTABLISHED 1333/java tcp6 0 0 192.168.88.104:3888 192.168.88.102:50104 ESTABLISHED 1333/java tcp6 0 0 192.168.88.104:3888 192.168.88.103:58344 ESTABLISHED 1333/java tcp6 0 0 192.168.88.104:48034 192.168.88.102:2888 ESTABLISHED 1333/java

通过上面的网络信息整体网络连接图如下

上图说明:
zookeeper Leader和Follower连接主要通过2888和3888,2888主要用于leader与follower进行工作,3888用于Leader选举。每个zookeeper节点都会与其他节点建立连接。

4.3.2 选举流程

情况一:第一次启动
以上面我们搭好的4台节点为例,myid和zxid情况如下:

节点名称 myid zxid q101 4 0 q102 3 0 q103 2 0 q104 1 0

选举前提当投票数量超过半数时才有效,此处4台节点,当第一次启动时候zxid都为0。只要有3台节点启动就可以选举出Leader。虽然说q101的myid=4但是如果最后启动q101,那么第一次启动时一定是q102成为Leader。

情况二:zxid不为0时,当集群重启,或Leader挂了的时候
当Leader挂了后,zookeeper集群会进行推选制选举,会优先选举数据最全的节点作为Leader(通过比较zxid,zxid越大代表该节点数据最全),如果zxid相同再比较myid。

节点名称 myid zxid q101 4 0x100000009 q103 2 0x100000010 q104 1 0x100000010

原Leader q102挂掉后无论哪个节点先发现Leader挂掉,都会推选zxid最大的节点,q103和q104 zxid 都为xxx10再比较myid。所以此处q103最终会被选为新的Leader。

5 watch
  1. Watch是轻量级的,其实就是本地JVM的Callback,服务器端只是存了是否有设置了Watcher的布尔类型。
  2. 在服务端,在FinalRequestProcessor处理对应的Znode操作时,会根据客户端传递的watcher变量,添加到对应的ZKDatabase(org.apache.zookeeper.server.ZKDatabase)中进行持久化存储,同时将自己NIOServerCnxn做为一个Watcher callback,监听服务端事件变化
  3. Leader通过投票通过了某次Znode变化的请求后,然后通知对应的Follower,Follower根据自己内存中的zkDataBase信息,发送notification信息给zookeeper客户端。
  4. Zookeeper客户端接收到notification信息后,找到对应变化path的watcher列表,挨个进行触发回调。
6 zookeeper实现分布式锁 6.1 实现原理

多个节点同时在一个指定的节点下面创建临时会话顺序节点,谁创建的节点序号最小,谁就获得了锁,并且其他节点就会监听序号比自己小的节点,一旦序号比自己小的节点被删除了,其他节点就会得到相应的事件,然后查看自己是否为序号最小的节点,如果是,则获取锁。

本文共计2540个文字,预计阅读时间需要11分钟。

1. 概述

1.1 简介

ZooKeeper 是 Apache 顶级项目之一,为分布式应用提供高效、高可用的分布式协调服务。它支持诸如数据发布/订阅、负载均衡、命名服务、分布式协调/锁以及通知和分布式锁等功能。

1 概述 1.1 简介

ZooKeeper 是 Apache 的一个顶级项目,为分布式应用提供高效、高可用的分布式协调服务,提供了诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知和分布式锁等分布式基础服务。由于 ZooKeeper 便捷的使用方式、卓越的性能(基于内存)和良好的稳定性,被广泛地应用于诸如 Hadoop、HBase、Kafka 和 Dubbo 等大型分布式系统中。
官方地址:zookeeper.apache.org

1.2 角色

领导者(leader):负责进行投票的发起和决议,更新系统状态。
跟随者(follower):用于接收客户端请求并给客户端返回结果,在选主过程中进行投票。
观察者(observer):可以接受客户端连接,将写请求转发给 leader,但是observer 不参加投票的过程,只是为了扩展系统,提高读取的速度。

1.3 节点特性

ZooKeeper 节点的生命周期取决于节点的类型。在 ZooKeeper 中,节点根据持续时间可以分为持久节点(PERSISTENT)临时节点(EPHEMERAL),根据是否有序可以分为顺序节点(SEQUENTIAL)、和无序节点(默认是无序的)。每个客户端连接zookeeper会产生一个session,客户端连接关闭时session也会消失
持久节点一旦被创建,除非主动移除,不然一直会保存在 Zookeeper 中(不会因为创建该节点的客户端的会话失效而消失)。

1.4 数据模型

层次化的目录结构,命名符合常规文件系统规范,类似于文件目录。
每个节点在 Zookeeper 中叫做 Znode,并且其有一个唯一的路径标识。
每个节点中数据存储不能超过1M(不要把Zookeeper当数据库用)。

1.5 特性

顺序一致性。客户端的更新将按发送顺序应用。
原子性。要么成功要失败。没有中间状态(最终一致性)。
单个系统映像。由于zookeeper使用复制集群,无论客户端连接哪个节点都能看到相同的数据。
可靠性。即所有节点支持持久化。
及时性。存储在节点中的数据会在较短时间内及时同步。

1.6 CAP

任何分布式架构都不能同时满足C(一致性)、A(可用性)、P(分区容错性)。zookeeper集群在保证一致性的同时,在A和P之间做了取舍,最终选择了P,因此可用性差一点。

2 zookeeper集群搭建 2.1 网络拓扑

2.2 环境准备

CentOS7 * 4
JDK8
Zookeeper3.6.3

2.3 安装

在q101服务器中配置好后发送到其他服务器。q101操作如下:

wget mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.6.3/apache-zookeeper-3.6.3-bin.tar.gz --no-check-certificate tar xf apache-zookeeper-3.6.3-bin.tar.gz mv apache-zookeeper-3.6.3-bin /usr/local/zookeepe cd /usr/local/zookeeper/conf mv zoo_sample.cfg zoo.cfg vi zoo.cfg # 1. 修改dataDir路径为“/var/zookeeper” # 2. 增加集群节点信息,信息如下 server.4=192.168.88.101:2888:3888 server.3=192.168.88.102:2888:3888 server.2=192.168.88.103:2888:3888 server.1=192.168.88.104:2888:3888 # 3. 保存退出 # 创建配置文件中设置的持久化目录 mkdir /var/zookeeper # 将节点信息中的节点ID写如myid中 echo 4 > /var/zookeeper/myid # 修改环境变量 增加zookeeper信息 # export ZOOKEEPER_HOME=/usr/local/zookeeper # 在export PATH末尾追加 :$ZOOKEEPER_HOME/bin vi /etc/profile source /etc/profile

详细配置说明

# leader和follower心跳时间,用于维护节点是否存活。单位毫秒 tickTime=2000 # 初始延迟。当follower追随leader时,leader允许初始延迟时间。 # 用tickTime乘以initLimit,此处为2000*10即20秒 initLimit=10 # 数据同步时间,及超过时间数据同步失败。 # 用tickTime乘以syncLimit,此处为2000*5即10秒 syncLimit=5 # 持久化目录,不建议存在tmp下 dataDir=/var/zookeeper # 客户端连接zookeeper端口号 clientPort=2181 # 允许客户端连接最大连接数 #maxClientCnxns=60 # dataDir中保留快照数量 #autopurge.snapRetainCount=3 # 清除任务时间间隔,单位小时。0表示禁用自动清楚功能 #autopurge.purgeInterval=1 # 自定义监控 #metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider #metricsProvider.baike.baidu.com/item/Paxos算法

4.2 ZAB协议

ZAB(ZooKeeper Atomic broadcast)即ZooKeeper原子消息广播协议,类似于一个二阶段提交过程(2PC)属于最终一致性。是zookeeper基于Paxos算法的简化实现。

所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为 Leader服务器,而余下的其他服务器则成为 Follower服务器。 Leader服务器负责将一个客户端事务请求转换成一个事务 Proposal(提议),并将该 Proposal分发給集群中所有的Follower服务器。之后 Leader服务器需要等待所有 Follower服务器的反馈,一旦超过半数的 Follower服务器进行了正确的反馈后,那么 Leader就会再次向所有的 Follower服务器分发 Commit消息,要求其将前一个 Proposal进行提交。

上图说明:
1:Client向Follower发出写操作;
2:Follower将收到的写请求转发给Leader处理;
3:Leader收到请求后分配一个全局单调递增的唯一的事务ID(即ZXID,按其先后顺序来进行排序与处理);
4.1:Leader服务器会为每一个 Follower服务器都各自分配一个单独的队列,然后将需要广播的事务Proposal依次放入这些队列中去,并且根据FIFO策略进行消息发送;每一个 Follower服务器在接收到这个事务 Proposal之后,都会首先将其以事务日志的形式写入到本地磁盘中去,并且在成功写入后反馈给 Leader服务器个Ack响应。Leader自己也会将事务日志写入磁盘;
4.2:当 Leader服务器接收到超过半数Follower的Ack响应后,就会广播一个Commit消息给所有的 Follower服务器以通知其进行事务提交(写入内存),同时 Leader自身也会完成对事务的提交。
5:由Leader将结果返回给Client1

上图中4.1和4.2步骤,由于Leader自身也计一票,如果Follower2 由于网络等原因没有给Leader Ack响应,但Leader和Follower 1 两票已超过半数,所以结果依然会成功。此时Client2请求Follower2获取“/aaa”数据时,可以通过sync 让Follower 2向Leader同步后再返回数据。

4.3 选举机制 4.3.1 节点网络

基于上面的第2章集群搭建,查看每台节点网络连接情况

yum install net-tools netstat -natp | egrep '(2888|3888)'

查看结果如下:

# q101信息 [root@q101 ~]# netstat -natp | egrep '(2888|3888)' tcp6 0 0 192.168.88.101:3888 :::* LISTEN 1924/java tcp6 0 0 192.168.88.101:58052 192.168.88.102:3888 ESTABLISHED 1924/java tcp6 0 0 192.168.88.101:39738 192.168.88.103:3888 ESTABLISHED 1924/java tcp6 0 0 192.168.88.101:54174 192.168.88.102:2888 ESTABLISHED 1924/java tcp6 0 0 192.168.88.101:47726 192.168.88.104:3888 ESTABLISHED 1924/java # q102信息 [root@q102 ~]# netstat -natp | egrep '(2888|3888)' tcp6 0 0 192.168.88.102:2888 :::* LISTEN 1661/java tcp6 0 0 192.168.88.102:3888 :::* LISTEN 1661/java tcp6 0 0 192.168.88.102:3888 192.168.88.101:58052 ESTABLISHED 1661/java tcp6 0 0 192.168.88.102:2888 192.168.88.103:38244 ESTABLISHED 1661/java tcp6 0 0 192.168.88.102:50104 192.168.88.104:3888 ESTABLISHED 1661/java tcp6 0 0 192.168.88.102:2888 192.168.88.104:48034 ESTABLISHED 1661/java tcp6 0 0 192.168.88.102:2888 192.168.88.101:54174 ESTABLISHED 1661/java tcp6 0 0 192.168.88.102:37466 192.168.88.103:3888 ESTABLISHED 1661/java # q103信息 [root@q103 ~]# netstat -natp | egrep '(2888|3888)' tcp6 0 0 192.168.88.103:3888 :::* LISTEN 1415/java tcp6 0 0 192.168.88.103:38244 192.168.88.102:2888 ESTABLISHED 1415/java tcp6 0 0 192.168.88.103:3888 192.168.88.101:39738 ESTABLISHED 1415/java tcp6 0 0 192.168.88.103:3888 192.168.88.102:37466 ESTABLISHED 1415/java tcp6 0 0 192.168.88.103:58344 192.168.88.104:3888 ESTABLISHED 1415/java # q104信息 [root@q104 ~]# netstat -natp | egrep '(2888|3888)' tcp6 0 0 192.168.88.104:3888 :::* LISTEN 1333/java tcp6 0 0 192.168.88.104:3888 192.168.88.101:47726 ESTABLISHED 1333/java tcp6 0 0 192.168.88.104:3888 192.168.88.102:50104 ESTABLISHED 1333/java tcp6 0 0 192.168.88.104:3888 192.168.88.103:58344 ESTABLISHED 1333/java tcp6 0 0 192.168.88.104:48034 192.168.88.102:2888 ESTABLISHED 1333/java

通过上面的网络信息整体网络连接图如下

上图说明:
zookeeper Leader和Follower连接主要通过2888和3888,2888主要用于leader与follower进行工作,3888用于Leader选举。每个zookeeper节点都会与其他节点建立连接。

4.3.2 选举流程

情况一:第一次启动
以上面我们搭好的4台节点为例,myid和zxid情况如下:

节点名称 myid zxid q101 4 0 q102 3 0 q103 2 0 q104 1 0

选举前提当投票数量超过半数时才有效,此处4台节点,当第一次启动时候zxid都为0。只要有3台节点启动就可以选举出Leader。虽然说q101的myid=4但是如果最后启动q101,那么第一次启动时一定是q102成为Leader。

情况二:zxid不为0时,当集群重启,或Leader挂了的时候
当Leader挂了后,zookeeper集群会进行推选制选举,会优先选举数据最全的节点作为Leader(通过比较zxid,zxid越大代表该节点数据最全),如果zxid相同再比较myid。

节点名称 myid zxid q101 4 0x100000009 q103 2 0x100000010 q104 1 0x100000010

原Leader q102挂掉后无论哪个节点先发现Leader挂掉,都会推选zxid最大的节点,q103和q104 zxid 都为xxx10再比较myid。所以此处q103最终会被选为新的Leader。

5 watch
  1. Watch是轻量级的,其实就是本地JVM的Callback,服务器端只是存了是否有设置了Watcher的布尔类型。
  2. 在服务端,在FinalRequestProcessor处理对应的Znode操作时,会根据客户端传递的watcher变量,添加到对应的ZKDatabase(org.apache.zookeeper.server.ZKDatabase)中进行持久化存储,同时将自己NIOServerCnxn做为一个Watcher callback,监听服务端事件变化
  3. Leader通过投票通过了某次Znode变化的请求后,然后通知对应的Follower,Follower根据自己内存中的zkDataBase信息,发送notification信息给zookeeper客户端。
  4. Zookeeper客户端接收到notification信息后,找到对应变化path的watcher列表,挨个进行触发回调。
6 zookeeper实现分布式锁 6.1 实现原理

多个节点同时在一个指定的节点下面创建临时会话顺序节点,谁创建的节点序号最小,谁就获得了锁,并且其他节点就会监听序号比自己小的节点,一旦序号比自己小的节点被删除了,其他节点就会得到相应的事件,然后查看自己是否为序号最小的节点,如果是,则获取锁。