ZooKeeper 学习笔记
简介
ZooKeeper 是一个开源的分布式协调服务,用于管理大规模分布式系统中的配置信息、命名服务、分布式同步和提供组服务等。ZooKeeper 提供了一个简单的分层命名空间和一个 API,使得使用者可以构建更为复杂的分布式系统。
ZooKeeper 主要负责以下几个方面:
-
维护配置信息:分布式系统中各个节点的配置信息都会通过 ZooKeeper 来维护。
-
维护节点状态:ZooKeeper 可以用来监控集群中的节点状态,例如节点的上下线状态。
-
实现分布式锁:ZooKeeper 的节点可以用来实现分布式锁,保证同一时刻只有一个节点能够执行某个任务。
-
实现集群领导选举:ZooKeeper 可以用来选举集群中的领导者。
安装
ZooKeeper 可以从官方网站下载安装包,也可以通过包管理器进行安装。
官方安装
-
下载 ZooKeeper:从官网 Apache ZooKeeper 下载最新版本的 ZooKeeper。
-
解压缩:将下载的文件解压到合适的目录下。
-
配置文件:修改 conf 目录下的 zoo.cfg 文件,根据需要进行相应的配置。
-
启动 ZooKeeper:在 bin 目录下执行 ./zkServer.sh start 命令即可启动 ZooKeeper。
包管理器安装
在 Linux 系统中,可以使用包管理器进行安装,例如在 Ubuntu 中可以使用 apt-get 命令进行安装:
sudo apt-get install zookeeperd
API
ZooKeeper 提供了一套 Java API,可以用来操作 ZooKeeper 中的数据。
创建 ZooKeeper 客户端
import org.apache.zookeeper.ZooKeeper; ZooKeeper zk = new ZooKeeper("localhost:2181", 5000, null);
创建节点
String path = "/path/to/node"; byte[] data = "hello world".getBytes(); List<ACL> acl = ZooDefs.Ids.OPEN_ACL_UNSAFE; CreateMode mode = CreateMode.PERSISTENT; String createdPath = zk.create(path, data, acl, mode);
获取节点数据
byte[] data = zk.getData(path, null, null); String str = new String(data);
更新节点数据
byte[] newData = "new data".getBytes(); int version = -1; Stat stat = zk.setData(path, newData, version);
删除节点
int version = -1; zk.delete(path, version);
监听节点变化
Watcher watcher = new Watcher() { public void process(WatchedEvent event) { System.out.println("Event: " + event.getType()); } }; byte[] data = zk.getData(path, watcher, null);
分布式锁(续)
创建锁
import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.CreateMode; import java.io.IOException; import java.util.Collections; import java.util.List; public class DistributedLock { private ZooKeeper zk; private String lockPath; private String lockName; private String myNode; private Watcher watcher; public DistributedLock(String connectString, String lockPath) throws IOException, KeeperException, InterruptedException { this.zk = new ZooKeeper(connectString, 5000, null); this.lockPath = lockPath; this.lockName = "lock-"; this.watcher = new Watcher() { public void process(WatchedEvent event) { synchronized (DistributedLock.this) { DistributedLock.this.notifyAll(); } } }; // 确保锁目录存在 Stat stat = zk.exists(lockPath, false); if (stat == null) { zk.create(lockPath, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } public void lock() throws KeeperException, InterruptedException { // 创建临时节点 myNode = zk.create(lockPath + "/" + lockName, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); while (true) { // 获取所有节点 List<String> nodes = zk.getChildren(lockPath, false); Collections.sort(nodes); // 判断当前节点是否是最小节点 int index = nodes.indexOf(myNode.substring(lockPath.length() + 1)); if (index == 0) { return; } // 监听前一个节点 String prevNode = nodes.get(index - 1); Stat stat = zk.exists(lockPath + "/" + prevNode, watcher); if (stat == null) { continue; } // 等待前一个节点被删除 synchronized (this) { wait(); } } } public void unlock() throws KeeperException, InterruptedException { zk.delete(myNode, -1); } }
使用锁
import org.apache.zookeeper.KeeperException; import java.io.IOException; public class Example { public static void main(String[] args) { try { DistributedLock lock = new DistributedLock("localhost:2181", "/path/to/lock"); lock.lock(); // 执行任务 lock.unlock(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (KeeperException e) { e.printStackTrace(); } } }
集群领导选举
ZooKeeper 可以用来实现集群中的领导选举。在 ZooKeeper 中,每个节点都可以创建一个临时顺序节点,用于竞选领导者。领导者节点被选举出来后,其他节点会监听该节点,并在该节点被删除后重新选举。
创建选举
import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.CreateMode; import java.io.IOException; import java.util.Collections; import java.util.List; public class LeaderElection implements Watcher { private ZooKeeper zk; private String electionPath; private String myNode; private String leaderNode; private Watcher watcher; public LeaderElection(String connectString, String electionPath) throws IOException, KeeperException, InterruptedException { this.zk = new ZooKeeper(connectString, 5000, this); this.electionPath = electionPath; this.myNode = null; this.leaderNode = null; this.watcher = new Watcher() { public void process(WatchedEvent event) { synchronized (LeaderElection.this) { LeaderElection.this.notifyAll(); } } }; // 确保选举目录存在 Stat stat = zk.exists(electionPath, false); if (stat == null) { zk.create(electionPath, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } public void start() throws KeeperException, InterruptedException { while (true) { // 创建临时顺序节点 myNode = zk.create(electionPath + "/node-", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); // 获取所有节点 List<String> nodes = zk.getChildren(electionPath, false); Collections.sort(nodes); // 判断当前节点是否是最小节点 int index = nodes.indexOf(myNode.substring(electionPath.length() + 1)); if (index == 0) { // 当前节点成为领导者 leaderNode = myNode; return; } // 监听前一个节点 String prevNode = nodes.get(index - 1); Stat stat = zk.exists(electionPath + "/" + prevNode, watcher); if (stat == null) { continue; } // 等待前一个节点被删除 synchronized (this) { wait(); } } } public String getLeaderNode() { return leaderNode; } public void process(WatchedEvent event) { synchronized (this) { notifyAll(); } } }
使用选举
import org.apache.zookeeper.KeeperException; import java.io.IOException; public class Example { public static void main(String[] args) { try { LeaderElection election = new LeaderElection("localhost:2181", "/path/to/election"); election.start(); // 当前节点成为领导者 if (election.getLeaderNode().equals(election.myNode)) { // 执行领导者任务 } else { // 执行普通节点任务 } } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (KeeperException e) { e.printStackTrace(); } } }
总结
ZooKeeper 是一个可靠的、高效的分布式协调服务,它可以用于解决分布式系统中的一些常见问题,如分布式锁、集群领导选举、配置管理等。在使用 ZooKeeper 时需要注意以下几点:
-
ZooKeeper 的数据模型是基于树形结构的,可以使用路径进行访问和操作。
-
ZooKeeper 提供了多种类型的节点,包括持久节点、临时节点、有序节点等,可以根据需求进行选择。
-
ZooKeeper 提供了事件通知机制,可以通过监听节点的变化来获取数据。
-
ZooKeeper 保证了数据的一致性、可靠性和高效性,可以用于构建高可用、高性能的分布式系统。
当然,使用 ZooKeeper 也存在一些注意事项:
-
ZooKeeper 的数据存储在内存中,因此不适合存储大量的数据。
-
ZooKeeper 的读写性能可能会受到节点数目、客户端数量等因素的影响,需要进行调优。
-
ZooKeeper 的使用需要注意安全性,需要设置访问控制和认证等机制。
总之,ZooKeeper 是一个非常重要的分布式协调服务,可以帮助我们构建高可用、高性能的分布式系统。学习和掌握 ZooKeeper 的使用方法对于分布式系统开发者来说是非常重要的一项技能。