ZooKeeper 学习笔记

ZooKeeper 学习笔记

简介

ZooKeeper 是一个开源的分布式协调服务,用于管理大规模分布式系统中的配置信息、命名服务、分布式同步和提供组服务等。ZooKeeper 提供了一个简单的分层命名空间和一个 API,使得使用者可以构建更为复杂的分布式系统。

ZooKeeper 主要负责以下几个方面:

  • 维护配置信息:分布式系统中各个节点的配置信息都会通过 ZooKeeper 来维护。

  • 维护节点状态:ZooKeeper 可以用来监控集群中的节点状态,例如节点的上下线状态。

  • 实现分布式锁:ZooKeeper 的节点可以用来实现分布式锁,保证同一时刻只有一个节点能够执行某个任务。

  • 实现集群领导选举:ZooKeeper 可以用来选举集群中的领导者。

安装

ZooKeeper 可以从官方网站下载安装包,也可以通过包管理器进行安装。

官方安装

  1. 下载 ZooKeeper:从官网 Apache ZooKeeper 下载最新版本的 ZooKeeper。

  2. 解压缩:将下载的文件解压到合适的目录下。

  3. 配置文件:修改 conf 目录下的 zoo.cfg 文件,根据需要进行相应的配置。

  4. 启动 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 的使用方法对于分布式系统开发者来说是非常重要的一项技能。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值