分布式技术 - Zookeeper详解

1. Zookeeper概述

1.1 概述:

Zookeeper 它服务的不是商家和用户,而是应用程序 。应用程序注入到 Zookeeper中后,Zookeeper就可以为这些应用程序提供服务

  • 淘宝、饿了么、美团等等诸多应用都是 Zookeeper 的现实版本,是一个服务平台,以淘宝为例:
    • 商家必须入驻淘宝,这样淘宝用户才能看到店铺
    • 客户看商品,下订单购买,付款完成交易,一整套流程都是在淘宝这个服务平台上完成的。( 和商家实际上接触并不多 )
  • Zookeeper 是一个 开源 分布式 ( 多台服务器干一件事 ) ,并且为分布式应用 提供协调服务 的Apache项目

1.2 工作机制

  • 从设计模式角度上来看:zookeeper是基观 察者模式 设计的分布式服务管理框架

    • 观察者模式:简单来说,就是一个监督者监视所有对象,当一个对象被修改时,则会自动通知依赖它的对象
    • Zookeeper:简单来说,Zookeeper对于每一个入驻的服务/程序进行 监听 ,如果哪一个服务下线/上线,将通知客户端应用程序
  • 负责 存储 管理 ,一些大家都关心的数据。然后接受 观察者 (类似淘宝客户) 的注册,一旦这些数据发生变化, Zookeeper 通知 已经注册的那些观察者做出相应的反应。从而实现集群中类似的 主从管理模式

  • Zookeeper = 文件系统 + 通知机制

1.3 特点

  • 分布式 集群

    • 无论分布式还是集群,都是有很多 人(很多台服务器) 再做事情,

    • 例如:饭店招聘

      • 分布式:

        招了1个厨师,3个服务员,1个前台。虽然工作不一样,但是都是为这个饭店的运转工作

      • 集群:

        招聘3个服务员,但是这3个人的工作是一样的,这就是集群

    • 实际运用:

      • 淘宝 = 订单模块 + 用户模块 + 商品模块 + …,这就是分布式。
      • 双十一,订单量巨大,就会新增很多处理订单模块的服务器。这些,新增的服务器都是用处理订单的,就是集群

Feature:

  • zookeeper是 一个leader、多个follower 组成的集群

  • Leader是通过内部选举机制临时产生

    在这里插入图片描述

  • 集群中,只要有半数以上的节点存活,Zookeeper就能正常工作,比如:

    假设有10台服务器:

    • 挂了4台能够正常工作
    • 挂了5台及以上,Zookeeper服务就停止了
  • Zookeeper中的数据,具有 全局一致性 ,每台服务器都保存一分相同的数据副本。无论,客户端连接哪台服务器,数据都是一致的

  • 数据的跟新,具有 原子性 。要么成功,要么失败

  • 实时性,在一定的时间范围内,客户端都能读取到最新的数据

  • 更新的请求 按照顺序执行,会按照发向服务器请求的顺序,逐一执行

1.4 数据结构

在这里插入图片描述

  • 整体,可以看做是 树状结构 ,每一个节称为:Znode ( ZookeeperNode )
  • 每个Znode默认能够存储 1MB 元数据 ,每个Znode的路径都是唯一的
    • 元数据:Metadata,又称中介数据、中继数据,是一种 描述数据的数据 。简单来说,就是记录数据信息的数据

1.5 应用场景

Zookeeper提供了 统一命名服务 , 统一配置管理 , 统一集群管理 , 服务器节点动态上下线 , 软负载均衡

1.5.1 统一命名服务

  • 分布式环境下,需要对应用或者服务进行统一命名,便于识别

  • 例如:服务器IP地址不易记住,但是域名很容易记住

    通过域名去访问服务器

在这里插入图片描述

1.5.2 统一配置管理

  • 分布式环境下,每台服务器的 配置文件同步 是必须的。(如果服务器一多,不可能一台一台的改)
  • 通过Zookeeper进行统一配置管理:
    1. 能够将配置信息,写入到Zookeeper的某个节点上
    2. 每个客户端都会监听这个节点
    3. 一旦节点中的数据被修改,Zookeeper服务会通知没改客户端服务器,进行告知

1.5.3 服务器节点动态上下线

  • 当服务器入驻到Zookeeper后,Zookeeper会对其进行监听。客户端能够实时获取服务器的上下线变化

1.5.4 软负载均衡

  • Zookeeper会 记录 每台服务器的 访问次数 ,让访问量最小的服务器去处理最新的客户端请求

1.6 下载

zookeeper-3.6.0

在这里插入图片描述

2. 安装:

2.1 本地安装

现将Zookeeper的文件上传到虚拟机的 /opt 目录

2.1.1 解压

# 解压安装包
tar -zxvf apache-zookeeper-3.6.0-bin.tar.gz
# 查看
ll
# 解压后的文件名太长,改名为:zookeeper
mv apache-zookeeper-3.6.0-bin zookeeper
# 查看目录
ll

2.1.2修改配置文件

# 进入到配置文件夹中
cd /opt/zookeeper/conf

修改 zoo_sample.cfg 文件,但是不直接修改,复制一份取名为: zoo.cfg ,在此文件上修改

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ohAKhhP-1617932299530)(F:\TyporaNotes\picture\image-20210406150817929.png)]

# 复制 zoo_sample.cfg文件
cp zoo_sample.cfg zoo.cfg
# 打开 zoo.cfg
vim zoo.cfg

打开 zoo.cfg 后,修改配置:

# 存放数据的目录
dataDir=/opt/zookeeper/zkData
# 存放日志的目录
dataLogDir=/opt/zookeeper/zkLog 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fJbMQAs9-1617932299532)(F:\TyporaNotes\picture\image-20210406152327672.png)]

然后 ESC :wq 保存退出

注意 :配置后,要在 opt/zookeeper/ 这个目录下,创建 zkData zkLog 这两个目录

cd /opt/zookeeper
# 创建目录
mkdir zkData
mkdir zkLog

安装完成

2.2操作Zookeeper

# 进入 Zookeeper安装目录下的 `bin` 目录
cd /opt/zookeeper/bin
  1. 启动Zookeeper

    # 启动服务
    ./zkServer.sh start
    
  2. 查看当前 Zookeeper的启动状态

    ./zkServer.sh status
    
  3. 查看进程是否启动

jps
# 只要有 QuorumPeerMain ,说明Zookeeper启动成功

在这里插入图片描述

  1. 启动客户端

    ./zkCli.sh
    
    # 进入客户端后
    # 查看 所有Zookeeper的所有节点
    ls /
    
    # 退出客户端窗口
    quit
    
  2. 停止Zookeeper

    # 停止服务
    ./zkServer.sh stop
    

2.3 Zookeeper配置文件的参数详解

进入配置文件目录: /opt/zookeeper/conf

zoo_sample.cfg , 但是我们复制了一份: zoo.cfg ,来修改

  1. 查看配置文件

    vim zoo.cfg 
    
    • tickTime = 2000

      • 单位 毫秒 ,是Zookeeper服务器和客户端之间的心跳时间。
      • tickTime = 2000 ,即每隔 2s客户端就往服务器发送一次请求,证明还是联通状态
    • initLimit = 10

      • (集群中的)learder 和 follower 的 初始通信时限

      • 集群启动时 能容忍的最多心跳数

      • initLimit = 10,即 tickTime * initLimit = 10*2000 ms= 20s,也就是说 20s之内,没有心跳发出,那么视为失效的连接,leader 和 follower 彻底断开

    • syncLimit = 5

      • learder 和 follower 的 同步通信时限

      • 集群启动后,learder 和 follower 的最大响应时间

      • 如果超过 syncLimit * tickTime =5*2= 10s,那么 leader认为 follower已经挂掉,leader会将follower从服务器列表删除

    • dataDir = /opt/zookeeper/zkData

      • 存放Zookeeper数据的目录
    • dataLogDir = /opt/zookeeper/zkLog

      • 存放日志文件的目录
    • clientPort = 2181

      • 客户端需要访问Zookeeper的端口号

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-271829qy-1617932299535)(F:\TyporaNotes\picture\image-20210406162336132.png)]

3. Zookeeper内部原理

3.1 选举机制 ( 面试重点 )

  • 半数机制:

    集群中, 半数以上 的服务器存活,集群可用。所以,Zookeeper适合安装奇数台服务器

  • Leader的选举机制:

    在这里插入图片描述

3.2 节点类型

  • 持久型:

    • 持久化目录节点 :客户端和服务器断开连接后,创建的节点依然存在
    • 持久化顺序编号目录节点 :客户端和服务器断开连接后,创建的节点依然存在。创建Znode时会设置顺序标识,即在Znode名后附加一个值来标识顺序,例如:Znode001,Znode002…
  • 短暂型:

    • 临时目录节点 :客户端和服务器断开连接后,创建的节点会自动删除
    • 零时顺序编号目录节点 :客户端和服务器断开连接后,创建的节点会自动删除。创建Znode时会设置顺序标识,即在Znode名后附加一个值来标识顺序,例如:Znode001,Znode002…

    注意:顺序标识的序号,呈 i ++ 的增长。和数据库中自增类似

3.3 监听器原理( 面试重点 )

在这里插入图片描述

  1. 在main方法中,创建两个线程,一个负责 网络通信 ,一个负责 监听
  2. 监听事件会通 过网络通信 线程,发送给 Zookeeper
  3. Zookeeper获得注册的监听事件后,立刻将 监听事件 添加到 监听列表
  4. 当zookeeper监听到 数据变化 或者 路径变化 就会将这个消息发送 监听 线程
  5. 就客户端接收到,数据有变后,监听线程就会在 内部调用 process方法,做出相应操作 ( process的方法需要根据需求,自己实现 )
  • 常见的监听:
    • 监听:子节点增减的变化
    • 监听:节点数据的变化

3.4 写数据的流程

在这里插入图片描述

  1. 客户端发送一个 写入数据的请求(想在 Zookeeper的 server1 上写入数据)
  2. 如果 server1 不是Leader,那么 server1 会把请求进一步转发给 Leader
  3. 这个 Leader 会将写请求的操作,广播给每个server,server写数据操作执行成功后会通知 Leader
  4. Leader 接到 半数以上 的 server 写数据成功的通知,那么写入数据的操作成功
  5. 然后, Leader 会告诉 server1 写入数据成功
  6. server1 通知 客户端,写入数据成功,整个流程结束

4. Zookeeper 实战( 开发重点

4.1 分布式安装部署

集群思路 :先搞定一台服务器,再克隆出两台,形成3台服务器的集群(3台是最低标准)

4.1.1 安装Zookeeper

略…( 上文有介绍 )

4.1.2 配置服务器编号 - myid

  • /opt/zookeeper/zkData 中创建 myid 文件

    cd /opt/zookeeper/zkData/
    # 创建myid
    vim myid
    # i键进行编辑 (实际上是添加该服务器对应的编号:1 )
    1
    # Esc 
    :WQ
    # 查看myid文件
    cat myid
    

4.1.3 修改配置文件

上文是介绍了,配置文件是复制一份并且重名为 zoo.cfg

  • 打开配置文件

    # 进入bin目录 
    cd /opt/zookeeper/conf
    # 打开配置文件
    vim zoo.cfg
    
  • 修改配置文件:

    # ---------cluster(集群)-----------
    server.1=192.168.2.17:2888:3888
    server.2=192.168.2.18.82:2888:3888
    server.3=192.168.2.19:2888:3888
    

    参数解读 : server.A = B : C : D

    • A :我们之前配置的服务器编号( /opt/zookeeper/zkData/myid 中的值)
    • B :该服务器的IP地址
    • C :与集群中 Leader 服务器交换信息的端口
    • D :选举机制的专用端口 (万一leader服务器宕机,需要该端口来重新选举一个服务器作为 Leader)。该端口在选举时,进行服务器之间的相互通信
  • 查看配置文件是否修改成功:

    cat /opt/zookeeper/conf/zoo.cfg
    

4.1.4 配置其余两台服务器

  1. 找到存放虚拟机数据的目录 ( D:\Program Files\vm

    在这里插入图片描述

  2. D:\Program Files\vm 目录下创建 zk02 文件夹

  3. 将我们刚刚配置好的服务器数据目录中 所有的 .vmx 文件和 .vmdk 文件分别拷贝到 zk02 文件夹

    在这里插入图片描述

  4. 打开 虚拟机 —> 文件 —> 打开 ( 选择zk02下的 .vmx文件 )

  5. 开启虚拟机后,弹出对话框,选择 我已复制该虚拟机

  6. 进入系统后:

    • 修改Linux中的 IP地址
    • 修改 /opt/zookeeper/zkData/myid 为 2
    • 测试网是否能通: ping www.baidu.com
  7. 同理,按照相同的操作配置另外的服务器

4.1.5 集群操作

  1. 关闭每台服务器的防火墙

    • 防火墙的状态:

      • Active: active (running) :开启
      • Active: inactive (dead) :关闭
    • 常用命令:

      # 查看防火墙
      systemctl status firewalld
      
      # 开启防火墙
      systemctl start firewalld.service
      # 关闭防火墙
      systemctl stop firewalld.service
      
      # 设置开机自动启动防火墙
      systemctl enable firewalld.service
      # 关闭开机制动启动防火墙
      systemctl disable firewalld.service
      
      # 在不改变状态的条件下重新加载防火墙
      firewall-cmd --reload
      
  2. 关闭防火墙之后,启动第一台服务器

    # 进入bin目录
    cd /opt/zookeeper/bin/
    # 启动服务
    ./zkServer.sh start
    
  3. 查看启动状态

    cd /opt/zookeeper/bin/ 
    # 查看状态
    ./zkServer.sh status
    

    会发现有一个 Error

    在这里插入图片描述

  4. 注意:当启动两台服务器时,查看状态:

    • 第一台: mode: follower
    • 第二台: mode: leader

4.2 客户端命令行

  1. 启动客户端 ( Server1: 服务器编号为1 )

    cd /opt/zookeeper/bin
    # 启动
    ./zkCli.sh
    
  2. 显示所有操作命令: help

  3. 查看Znode中的所有内容: ls + path

    # 查看根目录下有哪些Znode
    ls /
    
  4. 查看当前节点的详细数据: ls -s + path

    # 查看根节点的详细数据
    ls -s /
    
  5. 创建普通节点: create

    # 在根目录下创建USA
    create /USA
    create /UK
    
  6. 在更目录 / 下创建,俄罗斯节点,并保存“普京” 数据到节点上

    create /Ru "pujing"
    
  7. 创建多级节点:

    • 在日本节点下,创建 东京节点(这时候日本节点必须存在,提前创建好,不然报错)
    # 先创建日本节点
    create /japan
    # 再在日本节点下, 创建东京节点
    create /japan/Tokyo
    
  8. 获得节点的值: get

    get /Ru
    
  9. 创建临时节点(断开连接后,删除): create -e

    create -e /YiLaKe
    
  10. 创建带序号的节点: create -s

    # 创建持久的顺序节点
    create -s /USA/city	# 执行3次
    
    # 创建临时的顺序节点
    create -s -e /USA/city
    
  11. 修改节点中的值: set

    set /japan/Tokyo-1
    
  12. 监听节点的值变化 子节点变化

    • 在server3服务器上,注册监听 /USA 的变化

      addwatch /USA
      
    • 在Server1 主机上修改 /USA 的数据

      set /USA "hot"
      
    • Server3会立刻响应:

      WatchedEvent state:SyncConnected type:NodeDataChanged path:/USA

    • 如果在Server1 的 /USA 下创建子节点 NewYork ,Server3会立刻响应

      WatchedEvent state:SyncConnected type:NodeCreated path:/USA/NewYork

  13. 删除: delete

  14. 递归参数: deleteall

    deleteall /USA
    

4.3 API应用

4.3.1 IDEA环境的搭建

  • 创建maven项目

  • 导入依赖

    <dependencies>
            <!-- org.apache.logging.log4j/log4j-core -->
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>2.13.3</version>
            </dependency>
    
            <!-- org.apache.zookeeper/zookeeper -->
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.6.0</version>
            </dependency>
    
            <!-- junit/junit -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>
    
  • 在resource目录下创建 log4j.properties

    
    

4.3.2 创建Zookeeper对象

其实就是创建Zookeeper的客户端

public ZooKeeper(
    String connectString,
    int sessionTimeout, 
    Watcher watcher
) 

参数详解:

  • connectString ip:port 这种格式,多个的话用逗号 , 隔开(不能有空格)
  • sessionTimeout
    • 一般设置60*1000,就是60s
    • 一定不能太小。因为Zookeeper加载集群环境,会因为性能等原因而延迟过高。如果设置过小,在没有创建好客户端,就会操作节点,发生 空指针异常
  • watcher
    • Watcher是一个监听器接口,但是这里需要的是 对象,所以用匿名内部类
    • 要重写process方法:客户端收到监听信息后,会在内部调用process方法,做出相应操作
public class TestZK {

    private String connectString =
            "192.168.2.17:2181," +
            "192.168.2.18:2181," +
            "192.168.2.19:2181";
    private int sessionTimeout = 120 * 1000;
    /**
     * 客户端对象
     */
    private ZooKeeper zkClient;

    @Test
    public void init() throws Exception{
        zkClient=new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            public void process(WatchedEvent watchedEvent) {
                System.out.println("监听到,并且正在处理");
            }
        });
    }
}

4.3.3 创建节点

// create方法, 客户端对象调用
public String create(
    String path, 
    byte[] data, 
    List<ACL> acl, 
    CreateMode createMode
)

参数详解:

  • path :节点路径
  • data: 节点存储的内容 (byte字节的数组,可以把 String getBytes 方法转换)
  • acl :节点的权限
    • 一个ACL就是一个 id-permission对 ,类似于 key-value 键值对
    • permission :是一个 int 表示的位码,每一位代表一种操作对应的允许状态,类似于Linux的文件权限,不同的是共有5种操作: CREATE READ WRITE DELETE ADMIN (admin 对应更改ACL权限)
      • OPEN_ACL_UNSAFE :创建开放节点,允许任意操作 ( 常用 )
      • READ_ACL_UNSAFE :创建自读节点
      • CREATOR_ALL_ACL :创建者才有全部权限
  • createMode :创建节点的类型,比如:持久型的,还是临时节点
    • CreateMode. PERSISTENT :持久节点
    • CreateMode. PERSISTENT_SEQUENTIA :持久带序号的节点
    • CreateMode. EPHEMERAL :临时节点
    • CreateMode. EPHEMERAL_SEQUENTIAL :临时带序号的节点
// 创建节点
String str = zkClient.create(
    "/usa",
    "要存储的数据".getBytes(), 
    ZooDefs.Ids.OPEN_ACL_UNSAFE,
    CreateMode.PERSISTENT
);

然后在服务端查看:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mLYiEgJY-1617932299543)(F:\TyporaNotes\picture\image-20210407180633868.png)]

节点创见成功

4.3.4 获取节点数据

public byte[] getData(
    String path,
    boolean watch, 
    Stat stat
)

参数详解:

  • String path :想要获取数据的节点的路径
  • boolean watch :是用来注册的通知接口,如果节点发生变更,就会通过当前的接口通知客户端。
  • Stat stat :是用来描述该数据节点的状态信息
// 获取数据
byte[] data = zkClient.getData("/usa", false, new Stat());
// 将获取到的数据,转为String
String str = new String(data);

4.3.4 修改节点:

public Stat setData(
    String path,
    byte[] data,
    int version
) 

参数详解:

  • String path :要修改的目标节点
  • byte[] data :修改的内容
  • int version :版本号 (在服务器上用: ls -s 查看)
// 修改 /usa 节点上的数据
 Stat stat = zkClient.setData(
     "/usa", 
     "usa节点的值已修改".getBytes(), 
     0
 );

4.3.5 删除节点

public void delete(String path, int version)

参数详解:

  • String path :要删除的目标节点
  • int version :版本号 (在服务器上用: ls -s 查看)
  zkClient.delete("/usa", 1);

4.3.6 获取子节点

public List<String> getChildren(String path, boolean watch)

参数详解:

  • String path :目标节点路径
  • boolean watch : 监听还是不监听

返回值:

  • List<String> :目标节点的所有子节点集合
List<String> list = zkClient.getChildren("/USA", false);

4.3.7 节点的监听

和获取字节点的方法一样,值时参数 watch 为true,变为了监听状态

// 监听根节点
List<String> list = zkClient.getChildren("/", true);

如果 / 目录下,有节点发生变化,就会被监听到,调用内部的 process 方法。 process 方法在我们创建 Zookeeper对象 时就会定义

4.3.8 节点的判断

public Stat exists(String path, boolean watch)

例子:

Stat stat = zkClient.exists("/YiLaKe", false);
// 判断
if (stat==null){
    System.out.println("节点不存在");
}else {
    System.out.println("节点存在");
}

4.4 实战模拟 — 美团商家的上下线

4.4.1 需求分析

  • 模拟美团的服务平台

    • 商家营业通知
    • 商家打烊通知
  • 提前在更节点下,创建好 /meituan 节点

4.4.2 代码

1. pom文件的配置

同 4.3.1…

2. 商家服务类

…待跟新

3. 客户类

…待跟新


版权声明:本文为qq_41076531原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。