Zookeeper(91)Zookeeper的典型应用场景

161 阅读4分钟

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能够有效地解决分布式系统中的协调问题,确保系统的高可用性和一致性。