ZooKeeper学习笔记

1 Zookeeper入门

1.1 Zookeeper是什么?

Zookeeper 是一个开源的分布式的,为分布式框架提供协调服务的 Apache 项目。

1.1.1 Zookeeper工作机制

在这里插入图片描述

1.1.2 Zookeeper的特点

在这里插入图片描述

1.1.3 数据结构

ZooKeeper 数据模型的结构与 Unix 文件系统、HDFS很类似,整体上可以看作是一棵树,每个节点称做一个 ZNode。每一个 ZNode 默认能够存储 1MB 的数据(存储空间很小,所以不能用来存储数据,可以用来存储简单的配置信息),每个 ZNode 都可以通过其路径唯一标识。
在这里插入图片描述

1.1.4 应用场景

提供的服务包括:统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡等。

  1. 统一命名服务
    在这里插入图片描述

  2. 统一配置管理
    在这里插入图片描述

  3. 统一集群管理
    在这里插入图片描述

  4. 服务器动态上下线
    在这里插入图片描述

  5. 软负载均衡
    在这里插入图片描述

1.2 下载地址

官网首页:zookeeper官网下载地址
下载截图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2 Zookeeper安装

2.1 本地安装

安装前准备:

  1. 安装好JDK
  2. 拷贝 apache-zookeeper-3.5.7-bin.tar.gz 安装包到 Linux 系统下
  3. 解压到指定目录
 tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz -C /opt/module/zk

在这里插入图片描述
4. 修改名称
mv apache-zookeeper-3.5.7 -bin/ zookeeper-3.5.7

配置修改

  1. 将/opt/module/zookeeper-3.5.7/conf 这个路径下的 zoo_sample.cfg 修改为 zoo.cfg;
 mv zoo_sample.cfg zoo.cfg
  1. 打开 zoo.cfg 文件,修改 dataDir 路径: vim zoo.cfg
    修改如下内容:
dataDir=/opt/module/zookeeper-3.5.7/zkData
  1. 在/opt/module/zookeeper-3.5.7/这个目录上创建 zkData 文件夹:mkdir zkData
    在这里插入图片描述

操作Zookeeper:

  1. 启动 Zookeeper:bin/zkServer.sh start

  2. 查看进程是否启动:jps
    在这里插入图片描述

  3. 查看状态:bin/zkServer.sh status
    在这里插入图片描述

  4. 启动客户端:bin/zkCli.sh

  5. 退出客户端: quit

  6. 停止 Zookeeper:bin/zkServer.sh stop

配置参数解析:
在这里插入图片描述

  1. tickTime = 2000:通信心跳时间,Zookeeper服务器与客户端,和客服端与客服端心跳时间,单位毫秒
    在这里插入图片描述

  2. initLimit = 10:LF初始通信时限。
    Leader和Follower初始连接时能容忍的最多心跳数(tickTime的数量),不能超过 tickTime*initLimit的时间。

  3. syncLimit = 5:LF同步通信时限
    Leader和Follower之间通信时间如果超过syncLimit * tickTime,Leader认为Follwer死掉,从服务器列表中删除Follwer。

  4. dataDir:保存Zookeeper中的数据
    注意:默认的tmp目录,容易被Linux系统定期删除,所以一般不用默认的tmp目录.

  5. clientPort = 2181:客户端连接端口,通常不做修改。

2.2 集群安装

  1. 集群规划
    我直接克隆两次hadoop104,此时我的集群有hadoop104、hadoop105、hadoop106并且它们都安装好了Zookeeper。

  2. 配置服务器编号
    在/opt/module/zookeeper-3.5.7/zkData 目录下创建一个 myid 的文件vi myid,并在文件中添加与 server 对应的编号
    (注意:上下不要有空行,左右不要有空格)。
    在这里插入图片描述

  3. 配置zoo.cfg文件
    打开 zoo.cfg 文件,并在原有的配置基础上增加如下配置

    配置参数解读

    server.A=B:C:D。
    

    A 是一个数字,表示这个是第几号服务器;
    集群模式下配置一个文件 myid,这个文件在 dataDir 目录下,这个文件里面有一个数据就是 A 的值,Zookeeper 启动时读取此文件,拿到里面的数据与 zoo.cfg 里面的配置信息比较从而判断到底是哪个 server。

    B 是这个服务器的地址;

    C 是这个服务器 Follower 与集群中的 Leader 服务器交换信息的端口;

    D 是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口。

  4. 同步 zoo.cfg 配置文件
    我使用的是自己编写的同步分发脚本,如果没有可以直接拷贝到其它机器中。 xsync zoo.cfg

  5. 集群操作

    1. 分别启动 Zookeeper,集群中所有机器都执行:bin/zkServer.sh start
      启动前面几个时会出现这种状态:
      在这里插入图片描述
      它是怎么知道有没有超过半数呢?在这里插入图片描述

    2. 查看状态:bin/zkServer.sh status
      在这里插入图片描述
      在这里插入图片描述
      3. 集群启动脚本

      1. 在 hadoop102 的/home/zjw/bin 目录下创建脚本 vim /home/zjw/bin/ zk.sh

        #!/bin/bash
        case $1 in
        "start"){
        for i in hadoop104 hadoop105 hadoop106
        do
         echo ---------- zookeeper $i 启动 ------------
        ssh $i "/opt/module/zk/zookeeper-3.5.7/bin/zkServer.sh start"
        done
        };;
        "stop"){
        for i in hadoop104 hadoop105 hadoop106
        do
         echo ---------- zookeeper $i 停止 ------------ 
        ssh $i "/opt/module/zk/zookeeper-3.5.7/bin/zkServer.sh stop"
        done
        };;
        "status"){
        for i in hadoop104 hadoop105 hadoop106
        do
         echo ---------- zookeeper $i 状态 ------------ 
        ssh $i "/opt/module/zk/zookeeper-3.5.7/bin/zkServer.sh status"
        done
        };;
        esac
        
      2. 增加脚本执行权限: chmod u+x zk.sh

      3. Zookeeper 集群启动脚本: zk.sh start

      4. Zookeeper 集群停止脚本:zk.sh stop

2.3 集群选取机制(重点)

2.3.1 集群第一次启动的时候

在这里插入图片描述

2.3.2 集群在启动后出现故障后的leader选举机制

在这里插入图片描述

3 客户端操作

3.1 客户端命令行操作

3.1.1 命令行语法

在这里插入图片描述

  1. 启动客户端在这里插入图片描述

  2. 显示所有命令: help在这里插入图片描述

  3. 查看当前节点信息在这里插入图片描述
    (1)czxid:创建节点的事务 zxid
      每次修改 ZooKeeper 状态都会产生一个 ZooKeeper 事务 ID。事务 ID 是 ZooKeeper 中所有修改总的次序。每次修改都有唯一的 zxid,例如: zxid1 小于zxid2,那么 zxid1 在 zxid2 之前发生。
    (2)ctime:znode 被创建的毫秒数(从 1970 年开始)
    (3)mzxid:znode 最后更新的事务 zxid
    (4)mtime:znode 最后修改的毫秒数(从 1970 年开始)
    (5)pZxid:znode 最后更新的子节点 zxid
    (6)cversion:znode 子节点变化号,znode 子节点修改次数
    (7)dataversion:znode 数据变化号
    (8)aclVersion:znode 访问控制列表的变化号
    (9)ephemeralOwner:如果是临时节点,这个是 znode 拥有者的 session id。如果不是临时节点则是 0。
    (10)dataLength:znode 的数据长度
    (11)numChildren:znode 子节点数量

3.1.2 节点类型(持久/短暂/有序号/无序号)

在这里插入图片描述

  1. 分别创建2个普通节点(永久节点 + 不带序号)
    在这里插入图片描述
    注意:创建节点时,要赋值

  2. 获得节点的值
    在这里插入图片描述

  3. 创建带序号的节点(永久节点 + 带序号)
    在这里插入图片描述
    如果原来没有序号节点,序号从 0 开始依次递增。如果原节点下已有 2 个节点,则再排序时从 2 开始,以此类推。

  4. 创建带序号的短暂节点
    在这里插入图片描述
    退出后,再查看当前节点情况:
    在这里插入图片描述在这里插入图片描述

  5. 修改节点数据值在这里插入图片描述

3.1.3 监听器原理

客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、节点删除、子目录节点增加删除)时,ZooKeeper 会通知客户端。监听机制保证 ZooKeeper 保存的任何的数据的任何改变都能快速的响应到监听了该节点的应用程序。

在这里插入图片描述
1)节点的值变化监听

  1. 在 hadoop104 主机上注册监听/sanguo 节点数据变化: get -w /sanguo
  2. 在 hadoop105 主机上修改/sanguo 节点的数据:set /sanguo "xisi"
  3. 观察 hadoop104 主机收到数据变化的监听
    在这里插入图片描述
    注:注意:在hadoop105再多次修改/sanguo的值,hadoop104上不会再收到监听。因为注册一次,只能监听一次。想再次监听,需要再次注册。

2)节点的子节点变化监听(路径变化)

  1. 在 hadoop104 主机上注册监听/sanguo 节点的子节点变化:ls -w /sanguo
  2. 在 hadoop105 主机/sanguo 节点上创建子节点: create /sanguo/jin "simayi"
  3. 观察 hadoop104 主机收到子节点变化的监听在这里插入图片描述
3.1.4 节点的删除
  1. 删除单个节点
    在这里插入图片描述
  2. 递归删除多个节点:
    在这里插入图片描述

3.2 客户端 API 操作

3.2.1 IDEA 环境搭建

前提:保证 hadoop102、hadoop103、hadoop104 服务器上 Zookeeper 集群服务端启动。

  1. 创建一个工程:zookeeper
  2. 添加pom文件
   <dependencies>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>RELEASE</version>
    </dependency>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.8.2</version>
    </dependency>
    <dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.5.7</version>
    </dependency>
    </dependencies>
  1. 拷贝log4j.properties文件到项目根目录
log4j.rootLogger=INFO, stdout 
log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c]- %m%n 
log4j.appender.logfile=org.apache.log4j.FileAppender 
log4j.appender.logfile.File=target/spring.log 
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout 
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c]- %m%n
  1. 创建包名com.zjw.zk
  2. 创建类名称zkClient
3.2.2 创建 ZooKeeper 客户端

连接ZK的服务器,并创建节点zjw

package com.zjw;

import org.apache.zookeeper.*;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

public class zkClient{
    // 注意:逗号前后不能有空格
    private static String connectString =
            "hadoop104:2181,hadoop105:2181,hadoop106:2181";
    private static int sessionTimeout = 2000;
    private ZooKeeper zkClient = null;

    @Before
    public void init() throws Exception {
        zkClient = new ZooKeeper(connectString, sessionTimeout, new
                Watcher() {
                    @Override
                    public void process(WatchedEvent watchedEvent) {
                        // 收到事件通知后的回调函数(用户的业务逻辑)
                        System.out.println(watchedEvent.getType() + "--"
                                + watchedEvent.getPath());
                        // 再次启动监听
                        try {
                            List<String> children = zkClient.getChildren("/",
                                    true);
                            for (String child : children) {
                                System.out.println(child);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
    }
    // 创建子节点
    @Test
    public void create() throws Exception {
// 参数 1:要创建的节点的路径; 参数 2:节点数据 ; 参数 3:节点权限 参数 4:节点的类型
         String nodeCreated = zkClient.create("/zjw", "shuaige".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }
}


在 hadoop104 的 zk 客户端上查看创建节点情况
在这里插入图片描述

4 服务器动态上下线监听案例

4.1 需求

某分布式系统中,主节点可以有多台,可以动态上下线,任意一台客户端都能实时感知
到主节点服务器的上下线。

4.2 需求分析

在这里插入图片描述

4.3 具体实现

  1. 先在集群上创建/servers 节点
  2. 在 Idea 中创建包名:com.zjw.zkcase1
  3. 服务器端向 Zookeeper 注册代码
    DistributeServer.java
package com.zjw.zkCase1;
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
public class DistributeServer {
    private static String connectString = "hadoop104:2181,hadoop105:2181,hadoop106:2181";
    private static int sessionTimeout = 2000;
    private ZooKeeper zk = null;
    private String parentNode = "/servers";
    // 创建到 zk 的客户端连接
    public void getConnect() throws IOException{
        zk = new ZooKeeper(connectString, sessionTimeout, new
                Watcher() {
                    @Override
                    public void process(WatchedEvent event) {
                    }
                });
    }
    // 注册服务器
    public void registServer(String hostname) throws Exception{
        String create = zk.create(parentNode + "/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE,
                CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println(hostname +" is online "+ create);
    }
    // 业务功能
    public void business(String hostname) throws Exception{
        System.out.println(hostname + " is working ...");
        Thread.sleep(Long.MAX_VALUE);
    }
    public static void main(String[] args) throws Exception {
        // 1 获取 zk 连接
        DistributeServer server = new DistributeServer();
        server.getConnect();
        // 2 利用 zk 连接注册服务器信息
        server.registServer(args[0]);
        // 3 启动业务功能
        server.business(args[0]);
    } }

DistributeClient .java

package com.zjw.zkCase1;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
public class DistributeClient {
    private static String connectString = "hadoop104:2181,hadoop105:2181,hadoop106:2181";
    private static int sessionTimeout = 2000;
    private ZooKeeper zk = null;
    private String parentNode = "/servers";
    // 创建到 zk 的客户端连接
    public void getConnect() throws IOException {
        zk = new ZooKeeper(connectString, sessionTimeout, new
                Watcher() {
                    @Override
                    public void process(WatchedEvent event) {
                        // 再次启动监听
                        try {
                            getServerList();
                        } catch (Exception e) {
                            e.printStackTrace();
                        } }
                });
    }
    // 获取服务器列表信息
    public void getServerList() throws Exception {
        // 1 获取服务器子节点信息,并且对父节点进行监听
        List<String> children = zk.getChildren(parentNode, true);
        // 2 存储服务器信息列表
        ArrayList<String> servers = new ArrayList<>();
        // 3 遍历所有节点,获取节点中的主机名称信息
        for (String child : children) {
            byte[] data = zk.getData(parentNode + "/" + child, false, null);
            servers.add(new String(data));
        }
        // 4 打印服务器列表信息
        System.out.println(servers);
    }
    // 业务功能
    public void business() throws Exception{
        System.out.println("client is working ...");
        Thread.sleep(Long.MAX_VALUE);
    }
    public static void main(String[] args) throws Exception {
        // 1 获取 zk 连接
        DistributeClient client = new DistributeClient();
        client.getConnect();
        // 2 获取 servers 的子节点信息,从中获取服务器信息列表
        client.getServerList();
        // 3 业务进程启动
        client.business();
    } }

4.4 测试

先只测试DistributeClient 类,启动该类,然后在hadoop105上动态创建和删除节点,以此来模拟节点的上下线
在这里插入图片描述
在这里插入图片描述

接着在测试DistributeServer类,这次不用在虚拟机中自己去创建节点,而实启动带有参数的DistributeServer来模拟。

再Client启动的前提下,启动DistributeServer。

先启动Client类时
在这里插入图片描述

启动Server的步骤:
在这里插入图片描述
在这里插入图片描述
启动后:
Server类的控制台打印出:

在这里插入图片描述
Client控制台打印:
在这里插入图片描述
停止Server后:
在这里插入图片描述
可是接着继续多次的启动与停止Server类来模拟节点的动态上下线

5 ZooKeeper 分布式锁案例

什么叫做分布式锁呢?
比如说"进程 1"在使用该资源的时候,会先去获得锁,"进程 1"获得锁以后会对该资源保持独占,这样其他进程就无法访问该资源,"进程 1"用完该资源以后就将锁释放掉,让其他进程来获得锁,那么通过这个锁机制,我们就能保证了分布式系统中多个进程能够有序的访问该临界资源。那么我们把这个分布式环境下的这个锁叫作分布式锁。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值