结构
Zookeeper 是 Apache Hadoop 项目下的一个子项目,是一个树形目录服务。
ZooKeeper 的树形结构不仅是数据组织形式,更是其实现分布式协调功能的基础。服务注册、配置管理、Leader 选举和分布式锁等功能,本质上都是通过树节点的创建、删除、监听和顺序管理来实现的。
节点
| 类型 | 是否自动删除 | 是否带编号 |
|---|---|---|
| 持久节点(Persistent) | ❌ | ❌ |
| 临时节点(Ephemeral) | ✔ | ❌ |
| 持久顺序节点(Persistent Sequential) | ❌ | ✔ |
| 临时顺序节点(Ephemeral Sequential) | ✔ | ✔ |
是否永久就是客户端断开是否自动删除
部署
单机
Apache ZooKeeper 是:一个Java服务程序
解压后目录结构是bin/ conf/ logs/ lib/
创建conf/zoo.cfg配置文件
shell
tickTime=2000#心跳检测、Session 超时、Leader 选举节奏、Follower 同步节奏(毫秒)
dataDir=/tmp/zookeeper#ZooKeeper 持久化数据存放位置
clientPort=2181#客户端连接 ZooKeeper 的入口端口
dataDir=/data/zookeeper#生产环境改成这个,因为Linux可能自动清理/tmp
#或者详细点区分
ataDir=/data/zookeeper/data
dataLogDir=/data/zookeeper/log
initLimit=10 #Follower 初始化同步 Leader 的最大时间
syncLimit=5 #Follower 与 Leader 心跳同步的最大延迟容忍
#initLimit 和 syncLimit 只针对 ZooKeeper 自己的集群(ZK集群内部)
bin/zkServer.sh start 启动
bin/zkServer.sh status 查看状态
bin/zkServer.sh stop 停止
集群
必须"多数派存活"才算集群可用(Quorum机制)
shell
#每台机器都一样:
tickTime=2000
dataDir=/data/zookeeper
clientPort=2181#客户端连接
server.1=192.168.1.10:2888:3888
server.2=192.168.1.11:2888:3888
server.3=192.168.1.12:2888:3888
#server.x表示节点编号
#server.编号=IP:Follower通信端口:选举端口
#每台机器必须有 myid,myid不是目录,是文件,里面的内容就是id值
在 dataDir 目录下:/data/zookeeper/myid
选Leader
ZooKeeper 集群启动后通过 ZAB 协议进行 Leader 选举,优先比较 epoch,其次比较 zxid(事务ID),最后比较 myid,最终由拥有最新数据的多数节点共同选出 Leader,其他节点作为 Follower 同步数据。
zxid = ZooKeeper Transaction ID(事务ID)
数据"写操作"的顺序编号
每一次写操作都会生成一个 zxid
ZooKeeper 自动维护
epoch(任期号)
由 ZooKeeper 集群自动生成
保证: 新Leader的数据一定比旧Leader更新
ZAB
ZAB 协议(ZooKeeper Atomic Broadcast)
保证 ZooKeeper 集群数据一致性的广播协议
ZAB 协议是 ZooKeeper 的一致性协议,分为崩溃恢复和原子广播两个阶段,通过 Leader 选举、事务 zxid 顺序编号以及多数派确认机制,保证分布式环境下的数据强一致性。
| 目标 | 说明 |
|---|---|
| 1️⃣ 一致性 | 所有节点数据一致 |
| 2️⃣ 顺序性 | 写操作有全局顺序 |
| 3️⃣ 高可用 | Leader挂了能恢复 |
服务注册
yml
#springcloud
spring:
application:
name: user-service
cloud:
zookeeper:
connect-string: 127.0.0.1:2181
#如果是集群
connect-string: 192.168.1.10:2181,192.168.1.11:2181,192.168.1.12:2181
#也可以设置心跳和session超时
session-timeout: 30000
connection-timeout: 10000
ZooKeeper 集群中的每个节点都会同步保存完整的注册数据,
包括服务节点、地址和状态信息;
写请求统一由 Leader 处理,再同步到 Follower,以保证强一致性。
所以就是,springcloud部署到zookeeper集群,每个zookeeper都有一样的springcloud信息。
springcloud集群在zookeeper里没有主从
Spring Cloud Zookeeper默认使用临时节点
Spring Cloud Zookeeper实现了默认注册路径
Spring Cloud Zookeeper默认设置了心跳和session超时
Spring Cloud Zookeeper 已经帮你封装好了Watcher
Curator
Apache Curator 是一个"专门用来简化 ZooKeeper 使用的客户端框架"。
服务注册
java
//创建客户端
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(30000)
.connectionTimeoutMs(30000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
//创建节点
client.create()
.creatingParentsIfNeeded()
.forPath("/services/user-service", "online".getBytes());
Leader选举
Leader 选举选的不是普通业务服务
也不是 ZooKeeper 本身
而是"某一类需要主节点角色的服务实例"
计数器
Curator 提供 DistributedAtomicInteger 和 SharedCount 两种分布式计数器实现,本质基于 ZooKeeper 的 Znode + version CAS 机制,通过重试保证并发安全,从而避免手动实现读改写冲突问题。
Barrier
Curator 的 Barrier(DistributedBarrier)基于 ZooKeeper 节点状态实现分布式同步控制,所有参与节点在 waitOnBarrier 阻塞等待,当控制方调用 removeBarrier 删除屏障节点后,所有节点同时被唤醒继续执行,实现分布式"统一放行"机制。
分布式锁
java
InterProcessMutex lock = new InterProcessMutex(client, "/lock");
lock.acquire(); // 加锁
lock.release(); // 解锁
配置中心
java
NodeCache cache = new NodeCache(client, "/config/db.url");
cache.getListenable().addListener(() -> {
String newValue = new String(cache.getCurrentData().getData());
System.out.println("配置变更:" + newValue);
});
cache.start();
强一致性
所有"不能出错的协调类功能"都依赖强一致:
1️⃣ 分布式锁
2️⃣ Leader 选举
3️⃣ 配置中心
4️⃣ 注册/发现(服务目录一致性)
强一致说的"数据",本质就是 ZooKeeper 内部 Znode 上的数据 + 节点状态变化
ZooKeeper 的强一致性保证的是注册中心内部数据和服务列表的全局一致性,但不保证客户端请求绝对不会命中失效实例;请求是否打到坏节点,取决于客户端缓存、负载均衡和网络延迟,只是 ZooKeeper 通过快速删除节点和 Watcher 机制将这一不一致窗口压缩到极短。