Zookeeper 是一个分布式协调服务,采用了 Leader-Follower 架构来保证数据的一致性和高可用性。集群中的每个节点都有一个特定的角色:Leader、Follower 和 Observer。以下是对这些角色的详细介绍和代码示例。
Leader、Follower 和 Observer 角色
-
Leader:
- 职责:Leader 是 Zookeeper 集群中的核心节点,负责处理所有写请求和协调事务的提交。Leader 通过 Zab(Zookeeper Atomic Broadcast)协议与 Follower 进行通信,确保数据的一致性。
- 选举:Leader 是通过选举机制选出的,集群启动时或当前 Leader 故障时会进行选举。
-
Follower:
- 职责:Follower 是 Zookeeper 集群中的从节点,负责处理读请求和转发写请求给 Leader。Follower 还参与 Leader 选举和数据同步(从 Leader 接收事务日志并应用到本地数据)。
- 同步:Follower 会定期与 Leader 进行数据同步,确保数据的一致性。
-
Observer:
- 职责:Observer 是一种特殊的节点,主要用于扩展集群的读取能力。Observer 不参与 Leader 选举和事务提交,只负责处理读请求。
- 用途:Observer 适用于需要扩展读请求处理能力但不希望增加写请求处理负担的场景。
代码示例
以下代码示例展示了如何配置和使用 Zookeeper 集群,包括 Leader、Follower 和 Observer 的角色配置。
1. Zookeeper 集群配置
假设我们有一个三节点的 Zookeeper 集群,其中两个节点作为 Follower,一个节点作为 Observer。配置文件示例如下:
zoo.cfg(每个节点的配置文件都是类似的,只是 myid 不同):
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/var/lib/zookeeper
clientPort=2181
# Server configurations
server.1=zk1:2888:3888
server.2=zk2:2888:3888
server.3=zk3:2888:3888:observer
tickTime:Zookeeper 的基本时间单位(毫秒),用于心跳和超时计算。initLimit:Follower 初始同步数据时允许的时间(tick 数)。syncLimit:Leader 和 Follower 之间进行心跳检测的最大时间间隔(tick 数)。dataDir:Zookeeper 存储快照和事务日志的目录。clientPort:客户端连接 Zookeeper 服务器的端口。server.x:集群节点配置,其中zk1、zk2和zk3是节点的主机名或 IP 地址,2888和3888分别是集群通信端口和选举端口。observer表示该节点是 Observer。
每个节点需要一个唯一的 myid 文件,放在 dataDir 目录下,内容为节点的 ID(如 1、2、3)。
2. 使用 Zookeeper 客户端进行操作
以下代码示例展示了如何使用 Zookeeper 客户端连接集群并进行基本操作。
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
public class ZookeeperClientExample {
private static final String ZK_ADDRESS = "zk1:2181,zk2:2181,zk3:2181";
private static final int SESSION_TIMEOUT = 3000;
private ZooKeeper zooKeeper;
public void connect() throws IOException {
zooKeeper = new ZooKeeper(ZK_ADDRESS, SESSION_TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("Successfully connected to Zookeeper");
}
}
});
}
public void createNode(String path, byte[] data) throws KeeperException, InterruptedException {
zooKeeper.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
public byte[] getNodeData(String path) throws KeeperException, InterruptedException {
return zooKeeper.getData(path, false, null);
}
public void updateNodeData(String path, byte[] data) throws KeeperException, InterruptedException {
zooKeeper.setData(path, data, -1);
}
public void deleteNode(String path) throws KeeperException, InterruptedException {
zooKeeper.delete(path, -1);
}
public void close() throws InterruptedException {
if (zooKeeper != null) {
zooKeeper.close();
}
}
public static void main(String[] args) throws Exception {
ZookeeperClientExample client = new ZookeeperClientExample();
client.connect();
String path = "/example_node";
byte[] data = "Hello Zookeeper".getBytes();
// Create node
client.createNode(path, data);
// Retrieve and print node data
byte[] retrievedData = client.getNodeData(path);
System.out.println("Data of node " + path + ": " + new String(retrievedData));
// Update node data
byte[] newData = "Updated data".getBytes();
client.updateNodeData(path, newData);
// Retrieve and print updated node data
byte[] updatedData = client.getNodeData(path);
System.out.println("Updated data of node " + path + ": " + new String(updatedData));
// Delete node
client.deleteNode(path);
client.close();
}
}
详细解释
-
连接 Zookeeper 集群:
- 在
connect方法中,创建一个新的 Zookeeper 客户端实例,并通过 Watcher 监听连接状态。连接地址包括所有集群节点的地址。
- 在
-
创建节点:
- 在
createNode方法中,使用zooKeeper.create方法创建一个新的节点。ZooDefs.Ids.OPEN_ACL_UNSAFE表示不安全的 ACL(任何人都可以访问),CreateMode.PERSISTENT表示持久节点。
- 在
-
读取节点数据:
- 在
getNodeData方法中,使用zooKeeper.getData方法读取节点数据。
- 在
-
更新节点数据:
- 在
updateNodeData方法中,使用zooKeeper.setData方法更新节点数据。
- 在
-
删除节点:
- 在
deleteNode方法中,使用zooKeeper.delete方法删除节点。
- 在
-
关闭连接:
- 在
close方法中,关闭 Zookeeper 客户端连接。
- 在
总结
Zookeeper 的架构由 Leader、Follower 和 Observer 三种角色组成,确保数据的一致性和高可用性。通过上述配置和代码示例,可以了解如何配置和使用 Zookeeper 集群,包括连接集群、进行基本的节点操作(创建、读取、更新和删除)以及角色的职责和配置。