ZooKeeper是一种分布式协调服务,提供了强一致性、高可用性和高性能的分布式协调机制。它适用于多种分布式系统的协调任务。以下是ZooKeeper的几个典型应用场景及其实现代码示例。
1. 分布式锁(Distributed Lock)
分布式锁是分布式系统中常见的需求,它确保多个节点在同一时间只允许一个节点执行某个关键操作。
实现原理:
- 使用ZooKeeper的临时顺序节点(Ephemeral Sequential Node)。
- 每个客户端尝试创建一个临时顺序节点,并获取所有顺序节点列表。
- 客户端检查自己是否是最小顺序节点,如果是,则获取锁;否则,监听比自己小的前一个节点的删除事件。
代码示例:
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
public class DistributedLock {
private ZooKeeper zooKeeper;
private String lockBasePath = "/locks";
private String lockName;
private String lockPath;
public DistributedLock(String connectString, String lockName) throws IOException, KeeperException, InterruptedException {
this.zooKeeper = new ZooKeeper(connectString, 3000, event -> {});
this.lockName = lockName;
ensureLockBasePath();
}
private void ensureLockBasePath() throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists(lockBasePath, false);
if (stat == null) {
zooKeeper.create(lockBasePath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
public void lock() throws KeeperException, InterruptedException {
lockPath = zooKeeper.create(lockBasePath + "/" + lockName + "_", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
List<String> children = zooKeeper.getChildren(lockBasePath, false);
Collections.sort(children);
String thisNode = lockPath.substring(lockBasePath.length() + 1);
int index = children.indexOf(thisNode);
if (index == 0) {
return; // Acquired lock
}
String previousNode = children.get(index - 1);
final Object lock = new Object();
zooKeeper.exists(lockBasePath + "/" + previousNode, event -> {
synchronized (lock) {
lock.notify();
}
});
synchronized (lock) {
lock.wait();
}
}
public void unlock() throws KeeperException, InterruptedException {
zooKeeper.delete(lockPath, -1);
lockPath = null;
}
public static void main(String[] args) throws Exception {
DistributedLock lock = new DistributedLock("localhost:2181", "testLock");
lock.lock();
System.out.println("Acquired lock");
// Critical section
lock.unlock();
System.out.println("Released lock");
}
}
2. 配置管理(Configuration Management)
ZooKeeper可以用来存储和管理分布式系统的配置信息,确保所有节点都能读取到最新的配置。
实现原理:
- 将配置信息存储在ZooKeeper的一个节点中。
- 所有客户端节点都监听该配置节点的变化。
- 当配置变化时,ZooKeeper通知所有客户端节点更新配置。
代码示例:
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
public class ConfigurationManager {
private ZooKeeper zooKeeper;
private String configPath = "/config";
public ConfigurationManager(String connectString) throws IOException {
this.zooKeeper = new ZooKeeper(connectString, 3000, event -> {
if (event.getType() == Watcher.Event.EventType.NodeDataChanged && event.getPath().equals(configPath)) {
try {
loadConfig();
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
});
}
public void createConfig(String config) throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists(configPath, false);
if (stat == null) {
zooKeeper.create(configPath, config.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
} else {
zooKeeper.setData(configPath, config.getBytes(), -1);
}
}
public void loadConfig() throws KeeperException, InterruptedException {
byte[] data = zooKeeper.getData(configPath, true, null);
String config = new String(data);
System.out.println("Loaded config: " + config);
}
public static void main(String[] args) throws Exception {
ConfigurationManager configManager = new ConfigurationManager("localhost:2181");
configManager.createConfig("initialConfig");
configManager.loadConfig();
// Simulate config change
configManager.createConfig("updatedConfig");
}
}
3. 集群管理(Cluster Management)
ZooKeeper可以用来管理和监控分布式系统中的节点,确保所有节点的状态都能被实时监控。
实现原理:
- 所有节点在启动时在ZooKeeper上创建一个临时节点,表示自己在线。
- 管理节点监听这些临时节点的变化,实时感知节点的上线和下线。
代码示例:
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.List;
public class ClusterManager {
private ZooKeeper zooKeeper;
private String clusterPath = "/cluster";
public ClusterManager(String connectString) throws IOException, KeeperException, InterruptedException {
this.zooKeeper = new ZooKeeper(connectString, 3000, event -> {
if (event.getType() == Watcher.Event.EventType.NodeChildrenChanged && event.getPath().equals(clusterPath)) {
try {
listNodes();
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
});
ensureClusterPath();
}
private void ensureClusterPath() throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists(clusterPath, false);
if (stat == null) {
zooKeeper.create(clusterPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
public void registerNode(String nodeName) throws KeeperException, InterruptedException {
zooKeeper.create(clusterPath + "/" + nodeName, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}
public void listNodes() throws KeeperException, InterruptedException {
List<String> children = zooKeeper.getChildren(clusterPath, true);
System.out.println("Cluster nodes: " + children);
}
public static void main(String[] args) throws Exception {
ClusterManager manager = new ClusterManager("localhost:2181");
manager.registerNode("node1");
manager.listNodes();
// Simulate another node registration
ClusterManager anotherManager = new ClusterManager("localhost:2181");
anotherManager.registerNode("node2");
anotherManager.listNodes();
}
}
4. Leader选举(Leader Election)
在分布式系统中,某些任务需要一个Leader来进行协调和管理,ZooKeeper可以用来实现Leader选举。
实现原理:
- 每个节点尝试创建一个临时顺序节点。
- 所有节点获取顺序节点列表,最小顺序节点的持有者即为Leader。
- 如果Leader节点失效,重新进行选举。
代码示例:
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
public class LeaderElection {
private ZooKeeper zooKeeper;
private String electionPath = "/election";
private String nodePath;
public LeaderElection(String connectString) throws IOException, KeeperException, InterruptedException {
this.zooKeeper = new ZooKeeper(connectString, 3000, event -> {
if (event.getType() == Watcher.Event.EventType.NodeChildrenChanged && event.getPath().equals(electionPath)) {
try {
electLeader();
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
});
ensureElectionPath();
}
private void ensureElectionPath() throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists(electionPath, false);
if (stat == null) {
zooKeeper.create(electionPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
public void participateInElection(String nodeName) throws KeeperException, InterruptedException {
nodePath = zooKeeper.create(electionPath + "/" + nodeName + "_", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
electLeader();
}
private void electLeader() throws KeeperException, InterruptedException {
List<String> children = zooKeeper.getChildren(electionPath, true);
Collections.sort(children);
String leaderNode = children.get(0);
if (nodePath.endsWith(leaderNode)) {
System.out.println("I am the leader");
} else {
System.out.println("I am a follower");
}
}
public static void main(String[] args) throws Exception {
LeaderElection participant1 = new LeaderElection("localhost:2181");
participant1.participateInElection("node1");
LeaderElection participant2 = new LeaderElection("localhost:2181");
participant2.participateInElection("node2");
}
}
5. 分布式队列(Distributed Queue)
ZooKeeper可以用来实现分布式队列,确保多个节点能够有序地进行任务处理。
实现原理:
- 使用ZooKeeper的顺序节点来表示队列中的任务。
- 生产者创建顺序节点表示任务加入队列。
- 消费者获取最小顺序节点进行任务处理,并删除节点表示任务完成。
代码示例:
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
public class DistributedQueue {
private ZooKeeper zooKeeper;
private String queuePath = "/queue";
public DistributedQueue(String connectString) throws IOException, KeeperException, InterruptedException {
this.zooKeeper = new ZooKeeper(connectString, 3000, event -> {});
ensureQueuePath();
}
private void ensureQueuePath() throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists(queuePath, false);
if (stat == null) {
zooKeeper.create(queuePath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
public void produce(String data) throws KeeperException, InterruptedException {
zooKeeper.create(queuePath + "/task_", data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
}
public String consume() throws KeeperException, InterruptedException {
List<String> children = zooKeeper.getChildren(queuePath, false);
if (children.isEmpty()) {
return null;
}
Collections.sort(children);
String firstNode = children.get(0);
String firstNodePath = queuePath + "/" + firstNode;
byte[] data = zooKeeper.getData(firstNodePath, false, null);
zooKeeper.delete(firstNodePath, -1);
return new String(data);
}
public static void main(String[] args) throws Exception {
DistributedQueue queue = new DistributedQueue("localhost:2181");
queue.produce("task1");
queue.produce("task2");
String task = queue.consume();
System.out.println("Consumed task: " + task);
task = queue.consume();
System.out.println("Consumed task: " + task);
}
}
总结
以上示例展示了ZooKeeper在分布式锁、配置管理、集群管理、Leader选举和分布式队列等典型应用场景中的使用方法。通过合理的设计和实现,ZooKeeper能够有效地解决分布式系统中的协调问题,确保系统的高可用性和一致性。