欢迎光临
我们一直在努力

ZooKeeper深度解析:分布式系统的协调内核

ZooKeeper深度解析:分布式系统的协调内核

    • 引言
    • 一、ZooKeeper核心概念
      • 1.1 你的理解验证
      • 1.2 ZooKeeper在分布式系统中的位置
    • 二、ZooKeeper的数据模型
      • 2.1 树形结构
      • 2.2 ZNode类型
      • 2.3 ZNode数据结构
    • 三、ZooKeeper的核心特性
      • 3.1 一致性保证
      • 3.2 通知机制(Watcher)
      • 3.3 会话机制
    • 四、ZooKeeper的典型应用场景
      • 4.1 命名服务
      • 4.2 配置管理
      • 4.3 集群管理
      • 4.4 分布式锁
    • 五、ZooKeeper与Spark的集成
      • 5.1 Spark集群的ZK应用
      • 5.2 Spark Streaming的ZK偏移量管理
    • 六、ZooKeeper集群部署
      • 6.1 集群角色
      • 6.2 集群部署示例
    • 七、ZooKeeper vs 其他协调工具
    • 八、总结

🌺The Begin🌺点点关注,收藏不迷路🌺

引言

在分布式系统中,多个节点协同工作面临着诸多挑战:一致性、单点故障、配置管理、集群选举等。ZooKeeper正是为解决这些问题而生的分布式协调服务框架。它像一个分布式系统的"神经系统",感知并管理着集群的状态变化。

一、ZooKeeper核心概念

1.1 你的理解验证

“Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题(解决单点故障问题)。Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和监控你存储的数据的状态变化,通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。总结: Zookeeper=文件系统+通知机制”

✅ 非常精准的总结! 你抓住了ZooKeeper的两个核心本质:

  • 文件系统:类似目录树的数据结构
  • 通知机制:监听数据变化并触发回调

1.2 ZooKeeper在分布式系统中的位置

#mermaid-svg-y4ZnvZ8IMc0sC5OI{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .error-icon{fill:#552222;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .marker.cross{stroke:#333333;}#mermaid-svg-y4ZnvZ8IMc0sC5OI svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y4ZnvZ8IMc0sC5OI p{margin:0;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .cluster-label text{fill:#333;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .cluster-label span{color:#333;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .cluster-label span p{background-color:transparent;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .label text,#mermaid-svg-y4ZnvZ8IMc0sC5OI span{fill:#333;color:#333;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .node rect,#mermaid-svg-y4ZnvZ8IMc0sC5OI .node circle,#mermaid-svg-y4ZnvZ8IMc0sC5OI .node ellipse,#mermaid-svg-y4ZnvZ8IMc0sC5OI .node polygon,#mermaid-svg-y4ZnvZ8IMc0sC5OI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .rough-node .label text,#mermaid-svg-y4ZnvZ8IMc0sC5OI .node .label text,#mermaid-svg-y4ZnvZ8IMc0sC5OI .image-shape .label,#mermaid-svg-y4ZnvZ8IMc0sC5OI .icon-shape .label{text-anchor:middle;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .rough-node .label,#mermaid-svg-y4ZnvZ8IMc0sC5OI .node .label,#mermaid-svg-y4ZnvZ8IMc0sC5OI .image-shape .label,#mermaid-svg-y4ZnvZ8IMc0sC5OI .icon-shape .label{text-align:center;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .node.clickable{cursor:pointer;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .arrowheadPath{fill:#333333;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-y4ZnvZ8IMc0sC5OI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y4ZnvZ8IMc0sC5OI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-y4ZnvZ8IMc0sC5OI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .cluster text{fill:#333;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .cluster span{color:#333;}#mermaid-svg-y4ZnvZ8IMc0sC5OI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-y4ZnvZ8IMc0sC5OI rect.text{fill:none;stroke-width:0;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .icon-shape,#mermaid-svg-y4ZnvZ8IMc0sC5OI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .icon-shape p,#mermaid-svg-y4ZnvZ8IMc0sC5OI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .icon-shape rect,#mermaid-svg-y4ZnvZ8IMc0sC5OI .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y4ZnvZ8IMc0sC5OI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-y4ZnvZ8IMc0sC5OI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-y4ZnvZ8IMc0sC5OI :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

分布式系统

ZooKeeper集群

协调

协调

协调

协调

Leader

HBase

Kafka

Spark

Hadoop YARN

Follower

Follower

二、ZooKeeper的数据模型

2.1 树形结构

在这里插入图片描述

2.2 ZNode类型

类型特点应用场景
持久节点 创建后永久存在 配置信息、元数据
临时节点 会话结束自动删除 服务注册、心跳检测
持久顺序节点 持久 + 顺序编号 分布式队列
临时顺序节点 临时 + 顺序编号 分布式锁、Leader选举

2.3 ZNode数据结构

// ZNode的内部结构
public class ZNode {
private String path; // 节点路径
private byte[] data; // 节点数据
private Stat stat; // 状态信息

// Stat包含的元数据
class Stat {
long czxid; // 创建时的zxid
long mzxid; // 最后修改时的zxid
long ctime; // 创建时间
long mtime; // 修改时间
int version; // 数据版本号
int cversion; // 子节点版本号
int aversion; // ACL版本号
long ephemeralOwner; // 临时节点所有者会话ID
int dataLength; // 数据长度
int numChildren; // 子节点数量
long pzxid; // 子节点最后修改zxid
}
}

三、ZooKeeper的核心特性

3.1 一致性保证

ZooKeeper通过ZAB协议保证分布式数据一致性:

Follower2

Follower1

Leader

Client

Follower2

Follower1

Leader

Client

#mermaid-svg-2aW0IiDRKxvs7XLQ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2aW0IiDRKxvs7XLQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2aW0IiDRKxvs7XLQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2aW0IiDRKxvs7XLQ .error-icon{fill:#552222;}#mermaid-svg-2aW0IiDRKxvs7XLQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2aW0IiDRKxvs7XLQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2aW0IiDRKxvs7XLQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2aW0IiDRKxvs7XLQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2aW0IiDRKxvs7XLQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2aW0IiDRKxvs7XLQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2aW0IiDRKxvs7XLQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2aW0IiDRKxvs7XLQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2aW0IiDRKxvs7XLQ .marker.cross{stroke:#333333;}#mermaid-svg-2aW0IiDRKxvs7XLQ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2aW0IiDRKxvs7XLQ p{margin:0;}#mermaid-svg-2aW0IiDRKxvs7XLQ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2aW0IiDRKxvs7XLQ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-2aW0IiDRKxvs7XLQ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-2aW0IiDRKxvs7XLQ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-2aW0IiDRKxvs7XLQ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-2aW0IiDRKxvs7XLQ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-2aW0IiDRKxvs7XLQ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-2aW0IiDRKxvs7XLQ .sequenceNumber{fill:white;}#mermaid-svg-2aW0IiDRKxvs7XLQ #sequencenumber{fill:#333;}#mermaid-svg-2aW0IiDRKxvs7XLQ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-2aW0IiDRKxvs7XLQ .messageText{fill:#333;stroke:none;}#mermaid-svg-2aW0IiDRKxvs7XLQ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2aW0IiDRKxvs7XLQ .labelText,#mermaid-svg-2aW0IiDRKxvs7XLQ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-2aW0IiDRKxvs7XLQ .loopText,#mermaid-svg-2aW0IiDRKxvs7XLQ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-2aW0IiDRKxvs7XLQ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-2aW0IiDRKxvs7XLQ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-2aW0IiDRKxvs7XLQ .noteText,#mermaid-svg-2aW0IiDRKxvs7XLQ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-2aW0IiDRKxvs7XLQ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2aW0IiDRKxvs7XLQ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2aW0IiDRKxvs7XLQ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2aW0IiDRKxvs7XLQ .actorPopupMenu{position:absolute;}#mermaid-svg-2aW0IiDRKxvs7XLQ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-2aW0IiDRKxvs7XLQ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2aW0IiDRKxvs7XLQ .actor-man circle,#mermaid-svg-2aW0IiDRKxvs7XLQ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-2aW0IiDRKxvs7XLQ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

写请求

生成事务Proposal

广播Proposal

广播Proposal

ACK

ACK

收到多数ACK,提交

提交通知

提交通知

返回成功

3.2 通知机制(Watcher)

// Watcher监听示例
public class ZkWatcherDemo implements Watcher {

private ZooKeeper zk;

public void watchNode(String path) throws Exception {
// 注册Watcher,监听节点变化
zk.getData(path, this, null);
}

@Override
public void process(WatchedEvent event) {
EventType type = event.getType();
String path = event.getPath();

switch (type) {
case NodeCreated:
System.out.println("节点被创建: " + path);
break;
case NodeDeleted:
System.out.println("节点被删除: " + path);
break;
case NodeDataChanged:
System.out.println("节点数据变化: " + path);
break;
case NodeChildrenChanged:
System.out.println("子节点变化: " + path);
break;
}

// 重新注册Watcher(一次性触发)
try {
zk.getData(path, this, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}

3.3 会话机制

// 会话管理
public class ZkSessionManager {

public static void main(String[] args) throws Exception {
// 创建会话
ZooKeeper zk = new ZooKeeper(
"localhost:2181", // 连接字符串
30000, // 会话超时时间
new Watcher() { // 默认Watcher
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.Expired) {
System.out.println("会话过期,需要重新连接");
}
}
}
);

// 获取会话状态
ZooKeeper.States state = zk.getState();
System.out.println("会话状态: " + state);

// 会话ID
long sessionId = zk.getSessionId();
System.out.println("会话ID: " + sessionId);

// 关闭会话
zk.close();
}
}

四、ZooKeeper的典型应用场景

4.1 命名服务

// 统一命名服务
public class NamingService {

private ZooKeeper zk;

// 注册服务
public void registerService(String serviceName, String address)
throws Exception {
String path = "/services/" + serviceName;

// 创建服务节点(持久节点)
if (zk.exists(path, false) == null) {
zk.create(path, null,
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}

// 注册服务实例(临时顺序节点)
String instancePath = path + "/instance-";
zk.create(instancePath, address.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
}

// 发现服务
public List<String> discoverService(String serviceName)
throws Exception {
String path = "/services/" + serviceName;
return zk.getChildren(path, false);
}
}

4.2 配置管理

// 动态配置中心
public class ConfigCenter {

private ZooKeeper zk;
private Map<String, String> configCache = new ConcurrentHashMap<>();

public void init() throws Exception {
// 监听配置根节点
watchConfig("/config");
}

private void watchConfig(String path) throws Exception {
List<String> children = zk.getChildren(path, true);

for (String child : children) {
String fullPath = path + "/" + child;
// 监听每个配置节点的数据变化
byte[] data = zk.getData(fullPath, true, null);
if (data != null) {
configCache.put(fullPath, new String(data));
}
}
}

// 更新配置
public void setConfig(String key, String value) throws Exception {
String path = "/config/" + key;

if (zk.exists(path, false) == null) {
zk.create(path, value.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
} else {
zk.setData(path, value.getBytes(), 1);
}
}

// 获取配置
public String getConfig(String key) {
return configCache.get("/config/" + key);
}
}

4.3 集群管理

// 集群节点管理
public class ClusterManager {

private ZooKeeper zk;
private String clusterPath = "/cluster/nodes";
private String nodeId;

public void joinCluster(String nodeInfo) throws Exception {
// 创建临时顺序节点,表示加入集群
String path = clusterPath + "/node-";
nodeId = zk.create(path, nodeInfo.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);

System.out.println("节点加入集群: " + nodeId);

// 监控集群成员变化
watchClusterNodes();
}

private void watchClusterNodes() throws Exception {
List<String> nodes = zk.getChildren(clusterPath, true);
System.out.println("当前集群节点: " + nodes);
}

// 检测Master节点(Leader选举)
public String getMaster() throws Exception {
List<String> nodes = zk.getChildren(clusterPath, false);

if (nodes.isEmpty()) return null;

// 最小的节点是Master
Collections.sort(nodes);
return nodes.get(0);
}
}

4.4 分布式锁

// 分布式锁实现
public class DistributedLock {

private ZooKeeper zk;
private String lockPath = "/locks/my_lock";
private String lockNode;
private CountDownLatch latch = new CountDownLatch(1);

public void lock() throws Exception {
// 尝试创建临时顺序节点
lockNode = zk.create(lockPath + "/lock-", null,
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);

// 检查是否获得锁
tryLock();
}

private void tryLock() throws Exception {
List<String> nodes = zk.getChildren(lockPath, false);
Collections.sort(nodes);

String myNode = lockNode.substring(lockPath.length() + 1);
int index = nodes.indexOf(myNode);

if (index == 0) {
// 我是最小的节点,获得锁
return;
} else {
// 监听前一个节点
String prevNode = nodes.get(index 1);
String prevPath = lockPath + "/" + prevNode;

if (zk.exists(prevPath, true) != null) {
latch.await(); // 等待前一个节点释放
}
}
}

public void unlock() throws Exception {
// 删除节点,释放锁
zk.delete(lockNode, 1);
zk.close();
}

// Watcher处理
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.NodeDeleted) {
latch.countDown(); // 前一个节点释放,尝试获取锁
}
}
}

五、ZooKeeper与Spark的集成

5.1 Spark集群的ZK应用

#mermaid-svg-IzMeOqLRX93vxUF9{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IzMeOqLRX93vxUF9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IzMeOqLRX93vxUF9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IzMeOqLRX93vxUF9 .error-icon{fill:#552222;}#mermaid-svg-IzMeOqLRX93vxUF9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IzMeOqLRX93vxUF9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IzMeOqLRX93vxUF9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IzMeOqLRX93vxUF9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IzMeOqLRX93vxUF9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IzMeOqLRX93vxUF9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IzMeOqLRX93vxUF9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IzMeOqLRX93vxUF9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IzMeOqLRX93vxUF9 .marker.cross{stroke:#333333;}#mermaid-svg-IzMeOqLRX93vxUF9 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IzMeOqLRX93vxUF9 p{margin:0;}#mermaid-svg-IzMeOqLRX93vxUF9 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-IzMeOqLRX93vxUF9 .cluster-label text{fill:#333;}#mermaid-svg-IzMeOqLRX93vxUF9 .cluster-label span{color:#333;}#mermaid-svg-IzMeOqLRX93vxUF9 .cluster-label span p{background-color:transparent;}#mermaid-svg-IzMeOqLRX93vxUF9 .label text,#mermaid-svg-IzMeOqLRX93vxUF9 span{fill:#333;color:#333;}#mermaid-svg-IzMeOqLRX93vxUF9 .node rect,#mermaid-svg-IzMeOqLRX93vxUF9 .node circle,#mermaid-svg-IzMeOqLRX93vxUF9 .node ellipse,#mermaid-svg-IzMeOqLRX93vxUF9 .node polygon,#mermaid-svg-IzMeOqLRX93vxUF9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IzMeOqLRX93vxUF9 .rough-node .label text,#mermaid-svg-IzMeOqLRX93vxUF9 .node .label text,#mermaid-svg-IzMeOqLRX93vxUF9 .image-shape .label,#mermaid-svg-IzMeOqLRX93vxUF9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-IzMeOqLRX93vxUF9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IzMeOqLRX93vxUF9 .rough-node .label,#mermaid-svg-IzMeOqLRX93vxUF9 .node .label,#mermaid-svg-IzMeOqLRX93vxUF9 .image-shape .label,#mermaid-svg-IzMeOqLRX93vxUF9 .icon-shape .label{text-align:center;}#mermaid-svg-IzMeOqLRX93vxUF9 .node.clickable{cursor:pointer;}#mermaid-svg-IzMeOqLRX93vxUF9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IzMeOqLRX93vxUF9 .arrowheadPath{fill:#333333;}#mermaid-svg-IzMeOqLRX93vxUF9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IzMeOqLRX93vxUF9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IzMeOqLRX93vxUF9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IzMeOqLRX93vxUF9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IzMeOqLRX93vxUF9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IzMeOqLRX93vxUF9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IzMeOqLRX93vxUF9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IzMeOqLRX93vxUF9 .cluster text{fill:#333;}#mermaid-svg-IzMeOqLRX93vxUF9 .cluster span{color:#333;}#mermaid-svg-IzMeOqLRX93vxUF9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IzMeOqLRX93vxUF9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IzMeOqLRX93vxUF9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-IzMeOqLRX93vxUF9 .icon-shape,#mermaid-svg-IzMeOqLRX93vxUF9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IzMeOqLRX93vxUF9 .icon-shape p,#mermaid-svg-IzMeOqLRX93vxUF9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IzMeOqLRX93vxUF9 .icon-shape rect,#mermaid-svg-IzMeOqLRX93vxUF9 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IzMeOqLRX93vxUF9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IzMeOqLRX93vxUF9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IzMeOqLRX93vxUF9 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

Spark on YARN with ZooKeeper

选举

监控

ZooKeeper集群

ResourceManager Active

ResourceManager Standby

NodeManager 1

NodeManager 2

NodeManager 3

5.2 Spark Streaming的ZK偏移量管理

// Spark Streaming使用ZooKeeper存储偏移量
object ZKOffsetManager {

def saveOffsets(zkClient: ZooKeeper, groupId: String,
offsets: Map[TopicPartition, Long]): Unit = {
val path = s"/consumers/$groupId/offsets"

offsets.foreach { case (tp, offset) =>
val offsetPath = s"$path/${tp.topic}/${tp.partition}"

// 创建或更新偏移量节点
if (zkClient.exists(offsetPath, false) == null) {
zkClient.create(offsetPath, offset.toString.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)
} else {
zkClient.setData(offsetPath, offset.toString.getBytes(), 1)
}
}
}

def loadOffsets(zkClient: ZooKeeper, groupId: String,
topics: Array[String]): Map[TopicPartition, Long] = {
val offsets = mutable.Map[TopicPartition, Long]()

topics.foreach { topic =>
val path = s"/consumers/$groupId/offsets/$topic"
val children = zkClient.getChildren(path, false)

import scala.collection.JavaConverters._
children.asScala.foreach { partition =>
val offsetPath = s"$path/$partition"
val data = zkClient.getData(offsetPath, false, null)
val offset = new String(data).toLong
offsets += (new TopicPartition(topic, partition.toInt) -> offset)
}
}

offsets.toMap
}
}

六、ZooKeeper集群部署

6.1 集群角色

角色功能数量
Leader 处理写请求,协调集群 1
Follower 处理读请求,参与选举 N-1
Observer 只处理读请求,不参与选举 可选

6.2 集群部署示例

# zoo.cfg 配置文件
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper
clientPort=2181

# 集群节点配置
server.1=zk1:2888:3888
server.2=zk2:2888:3888
server.3=zk3:2888:3888

# 每个节点创建myid文件
# zk1上: echo 1 > /data/zookeeper/myid
# zk2上: echo 2 > /data/zookeeper/myid
# zk3上: echo 3 > /data/zookeeper/myid

七、ZooKeeper vs 其他协调工具

特性ZooKeeperetcdConsul
一致性协议 ZAB Raft Raft
数据模型 树形 Key-Value Key-Value
语言 Java Go Go
性能 读高写低 均衡 均衡
应用场景 大数据生态 云原生 服务发现

八、总结

维度说明
核心本质 分布式协调服务 = 文件系统 + 通知机制
数据模型 树形结构的ZNode节点
节点类型 持久、临时、顺序
核心特性 顺序一致性、原子性、单一系统映像、可靠性、实时性
应用场景 命名服务、配置管理、集群管理、分布式锁
与Spark关系 提供集群协调、元数据存储

核心要点:

  • 文件系统模型:ZooKeeper提供类似目录树的数据结构
  • 通知机制:Watcher监听数据变化,实现事件驱动
  • 一致性保证:ZAB协议确保分布式数据一致
  • 会话管理:临时节点与会话绑定,实现服务发现
  • 广泛集成:Hadoop、Spark、Kafka、HBase都依赖ZK
  • 一句话总结:ZooKeeper作为分布式系统的协调内核,通过简单的文件系统模型和强大的通知机制,为复杂分布式应用提供了一致性、可靠性的基础保障。

    在这里插入图片描述

    🌺The End🌺点点关注,收藏不迷路🌺

    赞(0)
    未经允许不得转载:171主机测评 » ZooKeeper深度解析:分布式系统的协调内核
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址