1、ZooKeeper概述

1.1、ZooKeeper 简介

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,

ZK 可以用来做什么

  • Dubbo和SpringCloud的注册中心
  • 实现分布式锁

1.2、ZooKeeper 内存数据模型

Zookeeper 提供了一个类似于 Linux 文件系统的树形结构(可认为是轻量级的内存文件系统,在zk当中,每一个节点都称为ZNode,每一个节点可以存储节点的路径以及响应的数据,但只适合存少量信息,完全不适合存储大量文件或者大文件),同时提供了对于每个节点的监控与 通知机制。

1.3、ZooKeeper 节点的分类

  • PERSISTENT(持久的)
  • EPHEMERAL(暂时的)
  • PERSISTENT_SEQUENTIAL(持久化顺序编号目录节点)
  • EPHEMERAL_SEQUENTIAL(暂时化顺序编号目录节点)

2、ZooKeeper的安装

2.1、Windows下安装

在进行安装zookeeper之前,我们需要对JDK进行安装。

下载安装包:https://zookeeper.apache.org/releases.html
选择一个版本进行下载:
在这里插入图片描述
下载完成后对压缩文件进行解压缩:
打开apache-zookeeper-3.6.1-bin\conf目录,把zoo_sample.cfg重命名成zoo.cfg
在这里插入图片描述
之后使用记事本打开这个文件,修改dataDir的值:原先:dataDir=/tmp/zookeeper 修改成dataDir=F:\Spark\Zookeep\Zookeep\apache-zookeeper-3.6.1-bin 文件解压的目录
在这里插入图片描述
修改完成后进行添加系统的环境变量:

  1. ZOOKEEPER_HOME
    F:\Spark\Zookeep\Zookeep\apache-zookeeper-3.6.1-bin
    在这里插入图片描述
    2 . 添加到path变量当中: %ZOOKEEPER_HOME%\bin
    在这里插入图片描述

全部配置完成后,打开cmd,运行zookeeper即可,使用zkserver
在这里插入图片描述
2.2、linux下安装

Zookeeper 3.5.7 版本:下载地址

1、上传下载好的gz压缩文件到虚拟机,并且进行解压到指定目录,解压后进行重命名

# 进行解压
tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz -C /opt/module/
# 重命名
mv apache-zookeeper-3.5.7-bin/ zookeeper-3.5.7

2、配置修改,将zookeeper-3.5.7/conf 这个路径下的 zoo_sample.cfg 修改为 zoo.cfg;打开 zoo.cfg 文件,修改 dataDir 路径:

# 重命名
mv zoo_sample.cfg zoo.cfg
# 打开文件
vim zoo.cfg
# 修改配置
dataDir=/opt/module/zookeeper-3.5.7/zkData
# 到指定的路径创建文件夹
mkdir zkData

3、Zookeeper的操作

# 启动zookeeper
zkServer.sh start
# 查看进程是否启动
jps
# 查看zookeeper当前状态
zkServer.sh status
# 启动客户端
zkCli.sh
# 停止zookeeper
zkServer.sh stop

2.3、使用 Docker 安装

docker pull zookeeper
docker run --name some-zookeeper --restart always -d zookeeper

2.4、集群搭建

在每台机器数据保持一致的情况下,zookeeper集群可以保证,客户端发起的每次查询操作,集群节点都能返回同样的结果。

但是对于客户端发起的修改、删除等能改变数据的操作呢?集群中那么多台机器,你修改你的,我修改我的,最后返回集群中哪台机器的数据呢?

这就是一盘散沙,需要一个领导,于是在zookeeper集群中,leader的作用就体现出来了,只有leader节点才有权利发起修改数据的操作,而follower节点即使接收到了客户端发起的修改操作,也要将其转交给leader来处理,leader接收到修改数据的请求后,会向所有follower广播一条消息,让他们执行某项操作,follower 执行完后,便会向 leader 回复执行完毕。当 leader 收到半数以上的 follower 的确认消息,便会判定该操作执行完毕,然后向所有 follower 广播该操作已经生效。

所以zookeeper集群中leader是不可缺少的,但是 leader 节点是怎么产生的呢?其实就是由所有follower节点选举产生的,讲究民主嘛,而且leader节点只能有一个,毕竟一个国家不能有多个总统。这也是为什么zk集群搭建的服务器需要是单数的原因。

下面就直接来搭建集群,这里就直接在同一台服务器上搭建了,对于多个服务器搭建也是一样的。

# 创建三个data
mkdir zkdata1 zkdata2 zkdata3
# 往三个目录中写入myid文件
touch zkdata1/myid zkdata2/myid zkdata3/myid

# 直接往文件当中输入内容
echo "1" >> zkdata1/myid
echo "2" >> zkdata2/myid
echo "3" >> zkdata3/myid

# 分别设置对应的配置文件
vim zkdata1/zoo.cfg

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tools/zkdata1
clientPort=3001
# 对于多台服务器只需要修改对应的ip地址即可
server.1=192.168.101.128:3002:3003
server.2=192.168.101.128:4002:4003
server.3=192.168.101.128:5002:5003

vim zkdata2/zoo.cfg

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tools/zkdata2
clientPort=4001
server.1=192.168.101.128:3002:3003
server.2=192.168.101.128:4002:4003
server.3=192.168.101.128:5002:5003

vim zkdata3/zoo.cfg

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tools/zkdata3
clientPort=5001
server.1=192.168.101.128:3002:3003
server.2=192.168.101.128:4002:4003
server.3=192.168.101.128:5002:5003

# 切换目录,直接加载配置文件进行启动
cd tools/zookeeper-3.6.1
./bin/zkServer.sh start /tools/zkdata1/zoo.cfg
./bin/zkServer.sh start /tools/zkdata2/zoo.cfg
./bin/zkServer.sh start /tools/zkdata3/zoo.cfg

# 查看节点的状态,leader 和 follower
./bin/zkServer.sh status /tools/zkdata1/zoo.cfg
./bin/zkServer.sh status /tools/zkdata2/zoo.cfg
./bin/zkServer.sh status /tools/zkdata3/zoo.cfg

# 连接服务进行操作
./bin/zkCli.sh -server 192.168.101.128:3001
./bin/zkCli.sh -server 192.168.101.128:4001
./bin/zkCli.sh -server 192.168.101.128:5001

2.5、配置文件详解

  • tickTime = 2000 通信心跳时间,Zookeeper服务器与客户端心跳时间,单位毫秒
  • initLimit = 10 集群初始通信时限
  • syncLimit = 5 集群同步通信时限(5次心跳时间)
  • dataDir 保存Zookeeper中的数据
  • clientPort = 2181 客户端连接端口,通常不做修改。
  • maxClientCnxns=60 最大的并发连接数
  • autopurge.snapRetainCount=3 每次快照数达到3个以上,会对快照进行合并
  • autopurge.purgeInterval=1 每隔一个小时进行合并

3、ZooKeeper 指令操作

3.1 、客户端指令操作

服务启动与连接

./bin/zkServer.sh start ./conf/zoo.cfg

./bin/zkCli.sh

客户端部分指令操作

# 查看路径下的节点
ls /

# 创建节点
create /node1 yueyue			# 默认 持久节点
create -s /node2 niao			# 持久顺序节点
create -e /node3 yue			# 临时节点
create -e -s /node3 huang		# 临时顺序节点

# 查看节点状态
stat /node1

# 获取节点数据
get /node1

# 修改节点数据
set /node1 niao

# ls2 等价于 ls + stat
ls2 /node1

# 删除节点,只能删除没有字节点的节点
delete /node1

# 删除所有节点
deleteall /node1

# 清除会话,这时的临时节点也会被清除掉。
quit

4、ZooKeeper 节点监听机制

客户端可以监听znode节点的变化,znode节点的变化触发相应的事件,然后清除对该节点的监测,当监测一个znode节点的时候,zookeeper会发送通知到监测节点,一个watch事件是一个一次性的触发器,当被设置了watch的数据和目录发生了改变的时候,服务器将这个改变发送给设置了watch的客户端,用来进行通知。

# 对路径进行监听
ls -w /node
# 对数据进行监听
get -w /node
# 监听节点创建、删除
stat -w /qun

5、使用Java 操作 ZooKeeper

这里还是使用maven项目来进行操作,首先构建一个maven项目,在项目当中添加依赖。

<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.10</version>
</dependency>

依赖添加之后就是编写java代码来对zk的节点进行操作,

import com.lzq.entity.User;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.List;

/**
 * @author yueyueniao
 * @version 1.0
 * @date 2020-12-07 21:25
 */
public class Test {
    public ZkClient zkClient;

    public static void main(String[] args) throws Exception {
        Test test = new Test();
        test.init();
        test.watchData();
    }

    public void init() {
    	// 连接到自己的zk服务
        zkClient = new ZkClient("192.168.101.128:2181", 60000 * 30, 60000, new SerializableSerializer());
        // 对于zk的集群操作,还是需要将所有的节点都写入进来,避免单个节点宕机导致服务无法操作集群
        // zkClient = new ZkClient("192.168.101.128:3001,192.168.101.128:4001,192.168.101.128:5001", 
        //				60000 * 30, 60000, new SerializableSerializer());
        System.out.println(zkClient);
    }

    public void createNode() {
    	// 创建节点,CreateMode.CONTAINER 节点类型(枚举类)
        zkClient.create("/node0", "yueyue", CreateMode.CONTAINER);
    }

    public void deleteNode() {
    	// 删除节点
        zkClient.delete("/node0");
        // 删除所有节点
        // zkClient.deleteRecursive("/node0");
    }

    public void getNode() {
    	// 获取节点
        zkClient.getChildren("/node0");
    }

    public void getNodeData() {
    	// 获取节点数据
        Object data = zkClient.readData("/node0");
    }

    public void getstat() {
    	// 获取节点状态
        Stat stat = new Stat();
        Object data = zkClient.readData("/node0", stat);
        System.out.println(stat.getCzxid());
    }

    public void update() {
    	// 更新节点,这里的User实体类也要进行序列化,
        User user = new User();
        user.setId(1);
        user.setName("niao");
        zkClient.writeData("/node0", user);
        User u = zkClient.readData("/node0");
        System.out.println(u.toString());
    }

    private void watchData() throws IOException {
    	// 对节点数据进行监听,使用匿名内部类对IZkDataListener接口实现,直接重新内部两个方法
        zkClient.subscribeDataChanges("/node0",new IZkDataListener(){
            public void handleDataChange(String dataPath, Object data) throws Exception{
                System.out.println(dataPath);
            }
            public void handleDataDeleted(String dataPath) throws Exception{
                System.out.println(dataPath);
            }
        });
        System.in.read();
    }
    
    private void watchChlid(){
      // 对节点路径进行监听,大致与数据监听一致,
      zkClient.subscribeChildChanges("/node0", new IZkChildListener() {
          public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
              System.out.println(parentPath);
          }
      });
    }
}

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐