吴志勇的博客 吴志勇的博客
  • h5

    • HTML5&CSS3
  • scss

    • css预处理语言
  • JavaScript

    • JavaScript教程
    • Ajax
    • ES6教程
    • NodeJS
    • Typescript
  • 框架

    • Jquery
    • VUE
    • React
  • Swing专题
  • java基础
  • javaweb
  • 框架
  • 数据库
  • netty
  • 设计模式
  • 微服务及架构
  • 云原生
  • maven
  • 单元测试
工具
我的
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

吴志勇

......
  • h5

    • HTML5&CSS3
  • scss

    • css预处理语言
  • JavaScript

    • JavaScript教程
    • Ajax
    • ES6教程
    • NodeJS
    • Typescript
  • 框架

    • Jquery
    • VUE
    • React
  • Swing专题
  • java基础
  • javaweb
  • 框架
  • 数据库
  • netty
  • 设计模式
  • 微服务及架构
  • 云原生
  • maven
  • 单元测试
工具
我的
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Swing专题

  • java基础

  • javaweb

  • 框架

  • Maven
  • 单元测试
  • 动态代理
  • 数据库

  • netty

  • 设计模式

  • 微服务及架构

    • Elasticsearch详细教程
    • Kafka教程
    • Zookeeper教程
      • Nginx
    • 云原生

    • Velocity模板引擎
    • 后端
    • 微服务及架构
    wuzhiyong
    2024-10-15

    Zookeeper教程

    # Zookeeper 详细教程

    # 一、Zookeeper 介绍

    # 1.1 什么是 zookeeper

    ​ Zookeeper 是一个分布式的、高性能的、开源的分布式系统的协调(Coordination)服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 Hbase 的一个重要的组件。它是一个为分布式应用提供一致性服务的软件。

    # 1.2 zookeeper 应用场景

    ​ zookeeper 是一个经典的分布式数据一致性解决方案,致力于为分布式应用提供一个高性能,高可用,且具有严格属性访问控制能力的分布式协调存储服务。

    • 维护配置信息
    • 分布式锁服务
    • 集群管理
    • 生成分布式唯一 ID

    1、维护配置信息

    ​ java 编程经常会遇到配置项,比如数据库的 url,schema,user 和 password 等。通常这些配置项我们会放置在配置文件中,在将配置文件放置在服务器上当需要更改配置的时,需要去服务器上修改对应的配置信息文件。但是随着分布式系统的兴起,由于许多服务都需要使用到该配置文件,因此有必须保证该配置服务的高可用性和各台服务器上配置数据的一致性。通常会将配置文件不俗在一个集群上,然而一个集群动辄上前台服务器,此时如果在一台一台服务器逐个的修改配置文件将是非常繁琐的一个操作。因此就需要一种服务,能够高效快速且可靠的完成配置项的更待等操作,并能够保证各个配置项在每一台服务器上的数据一致性。

    ​ zookeeper 就可以提供这样一种服务,其使用 Zab 这种一致性协议来保证一致性。现在有很多开源项目使用 zookeeper 来维护配置,比如 hhase 中,客户端就是连接一个 zookeeper,获得必要 hbase 集群的配置信息然后才可以进一步操作。还有开源的消息队列 kafka 中,也是用 zookeeper 来维护 broker 的信息。

    2、分布式锁服务

    ​ 一个集群是一个分布式系统,有多台服务器组成。为了提高并发度和可靠性,多台服务器运行着同一种服务。当多个服务在运行时就需要协调各服务的进度,有时候需要保证当某个服务在进行某个操作时,其他的服务都不能进行该操作,即对该操作进行加锁,如果当前机器挂掉后,并释放 fail over 到其他的机器继续执行该服务。

    image-20200513003823079

    3、集群管理

    ​ 一个集群优势会因为各种软硬件故障或者网络故障,出现某种服务器挂掉而被移除集群,而某些服务器加入到集群中的情况,zookeeper 会将这些服务器加入/移出的情况下通知给集群汇总的其他正常工作的服务器,以及时调用存储和计算等任务的分配和执行等。此外 zookeeper 还会对故障的服务器做出诊断并尝试修复。

    image-20200513004430681

    4、生成分布式唯一 ID

    ​ 在过去的单库单表型系统中,通常可以使用数据库字段自带的 auto_increment 属性来自动为每条记录生成一个唯一的 ID。但是分库分表后,就无法再依靠数据库的 auto_increatment 属性来唯一标识一条记录了,此时我们就可以用 zookeeper 在分布式环境下生成全局唯一 ID。做法如下:每一个生成一个新 ID 时,创建一个持久顺序节点,创建操作返回的节点序号,及微信 ID,然后把比自己节点小的删除即可。

    # 1.3 zookeeper 的设计目标

    ​ zookeeper 致力于为分布式应用提供一个高性能,高可用,具有严格顺序访问控制能力的分布式协调服务。

    1、高性能

    ​ zookeeper 将全量数据存储在内存中,并直接服务与客户端的所有非事务请求,尤其适合用于以读为主的应用场景。

    2、高可用

    ​ zookeeper 一般以集群的范式对外提供服务,一般 3-5 台机器就可以组成一个可用的 zookeeper 集群,每一台机器都会在内存中维护当前的服务器状态,并且每台机器之间都相互保持着通信。只要集群中超过一旦的机器都在工作,那么这个集群就能够正常对外服务;

    3、严格访问数据

    ​ 对于客户端的每一个更新请求,Zookeeper 都会分配一个全局唯一的递增编号,这个编号反映了所有事物操作的先后顺序。

    # 二、Zookeeper 的数据模型

    # 2.1 zookeeper 数据结构

    Zookeeper 数据模型的结构与 Unix 文件系统很类似,整体上可以看作是一颗树,每一个节点称做一个 ZNode。每一个 Znode 默认能够存储 1MB 的数据,每个 ZNode 都可以通过其路径唯一标识。

    image-20200513104945468

    如何来描述一个 ZNode 呢?一个 znode 大体上分为 3 部分:

    • 节点的数据:即 znode data(节点 path,节点 data 的关系)就像是 java map 中(key,value)的关系
    • 节点的子节点 children
    • 节点的状态 stat:用来描述当前节点的创建,修改记录,包括 cZxid、ctime 等。
    # 2.2 zookeeper 节点类型

    ​ zookeeper 中的节点有两种类型,一种是临时节点和永久节点。节点类型在创建是即被确定,并且不能改变。

    • 临时节点 :该节点的生命周期依赖于创建他们的回话。一旦回话(Session)结束,临时节点将会被自动删除,当然可以手动的进行删除。虽然每个临时的 ZNode 都会绑定到一个客户端回话,但他们对所有的客户端还是可见的。另外,Zookeeper 的临时节点不允许拥有子节点。
    • 持久化节点:该节点的生命周期不依赖于花花,并且只有在客户点显示执行删除操作的时候,他们才能被删除。
    # 2.3 zookeeper 单机安装

    ​ 当前测试系统环境 centos7.3

    1.在 centos 中使用 root 用户创建 zookeeper 用户,用户名:zookeeper 密码:zookeeper

    useradd zookeeper
    passwd zookeeper
    
    1
    2
    1. zookeeper 底层依赖 jdk,zookeeper 用户登录后,根目录下先进行 jdk 的安装,jdk 使用 jdk-11.0.5
    //解压jdk tar -xzvf jdk-11.0.5.tar.gz
    
    1

    3.配置 jdk 环境量

    //打开/etc/profile文件
    # vim /etc/profile
    export JAVA_HOME=/root/apps/jdk-11.0.5
    export PATH=$PATH:$JAVA_HOME/bin
    //执行profile
    #source /etc/profile
    
    1
    2
    3
    4
    5
    6

    4.zookeeper 使用 zookeeper-3.4.10.tar.gz,上传并解压

    //解压zookeeper
    tar -xzvf zookeeper-3.4.10.tar.gz
    
    1
    2

    5.为 zookeeper 准备配置文件

    //进入conf目录
    cd /root/apps/zookeeper/zookeeper-3.4.14/conf
    //复制配置文件
    cp zoo_sample.cfg zoo.cfg
    //zookeeper根目录下新建data目录
    mkdir data
    // vi 修改配置文件中的dataDir
    // 此路径用于存储zookeeper中数据的内存快照,及事务日志文件
    dataDir=/root/apps/zookeeper/zookeeper-3.4.14/data
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    image-20200513230216216

    6.zookeeper 启动命令

    [root@centos ~]# systemctl stop firewalld #关闭防火墙的命令
    [root@centos ~]# zkServer.sh start  #启动
    [root@centos ~]# zkServer.sh stop   #关闭
    [root@centos ~]# zkServer.sh status #查看运行状态
    
    启动客户端:
    [root@localhost ~]# zkCli.sh   //启动客户端
    [zk: localhost:2181(CONNECTED) 0] ls /  #查看根节点命令
    //查看zookeeper进程是否存在
    [root@centos zookeeper-3.4.14]# ps -ef | grep zookeeper
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    # 三、zookeeper 常用的 Shell 命令

    # 3.1 新增节点
    create [-s] [-e] path data   # 其中 -s 为有序节点, -e 临时节点
    
    1

    1.创建持久化节点并写入数据:

    create /hadoop  "123456"
    
    1

    2.创建持久化有序节,此时创建的节点名为指定节点名+自增序号

    //创建一个持久化节点
    [zk: localhost:2181(CONNECTED) 1] create /hadoop "123456"
    //通过get命令获取该节点的值
    [zk: localhost:2181(CONNECTED) 2] get /hadoop
    //创建一个有序的节点
    [zk: localhost:2181(CONNECTED) 4] create -s /a "aa"
    Created /a0000000001
    //获取路径的值
    [zk: localhost:2181(CONNECTED) 5] get /a0000000001
    aa
    
    //创建临时节点并获取值
    [zk: localhost:2181(CONNECTED) 1] create -e /tmp "tmp"
    Created /tmp
    [zk: localhost:2181(CONNECTED) 2] get /tmp
    tmp
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    3.创建临时节点,临时节点会在回话过期后被删除;

    [zk: localhost:2181(CONNECTED) 1] create -e /tmp "tmp"
    Created /tmp
    
    
    1
    2
    3

    4.创建临时有序节点,临时节点会在会话过期后被删除:

    ```xml
    

    [zk: localhost:2181(CONNECTED) 1] create -s -e /tmp "tmp" Created /tmp000000001

    
    ##### 3.2 更新节点
    
    ```xml
    //使用set命令来更新hadoop节点
    [zk: localhost:2181(CONNECTED) 1] set /hadoop "345"
    //根据版本号来更新节点
    [zk: localhost:2181(CONNECTED) 1] set /hadoop "345" 2
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    ​ 也可以基于版本号来进行更改,此时类似于乐观锁机制,当你传入的数据版本号(dataVersion)和当前节点的数据版本号不符合时,zookeeper 会拒绝本次修改:

    # 3.3 删除节点

    1.删除节点的命令如下

    delete path [version]
    
    1

    和更新节点数据一样,也可以传入版本号,当你传入的数据版本号(dataVersion)和当前节点的数据版本号不符合时,zookeeper 不会执行删除操作。

    //根据版本号来删除节点
    [zk: localhost:2181(CONNECTED) 1] set /hadoop "345" 2
    
    1
    2

    要想删除某个节点及其所有后代节点,可以使用递归删除,命令为 rmr path。

    # 3.4 查看节点
    get path
    [zk: localhost:2181(CONNECTED) 1] get /hadoop
    
    1
    2
    [zk: localhost:2181(CONNECTED) 0] create /hadoop2 "hadoop"
    Created /hadoop2
    [zk: localhost:2181(CONNECTED) 1] get /hadoop2
    hadoop
    cZxid = 0xb
    ctime = Thu May 14 09:06:29 CST 2020
    mZxid = 0xb
    mtime = Thu May 14 09:06:29 CST 2020
    pZxid = 0xb
    cversion = 0
    dataVersion = 0
    aclVersion = 0
    ephemeralOwner = 0x0
    dataLength = 6
    numChildren = 0
    [zk: localhost:2181(CONNECTED) 2]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    ​ 节点各个属性如下表。起哄一个重要的概念是 Zxid(ZooKeeper Transaction Id),ZooKeeper 节点的每一个更改都具唯一的 Zxid,如果 Zxid1 小于 Zxid2,则 Zxid1 的更改发生在 Zxid2 更改之前。

    状态属性 节点说明
    cZxid 数据节点创建时的事务 ID
    ctime 数据节点创建世的时间
    mZxid 数据节点最后一个更新是的事务 ID
    mtime 数据节点最后一个跟新时的时间
    pZxid 数据节点的子节点最后一个被修改时的事务 ID
    cversion 子节点的更改次数
    dataVerion 节点数据的更改次数
    aclVersion 节点 ACL 的更改次数
    ephemeralOwner 如果节点是临时节点,则表示创建该节点的会话的 SeeesionID;如果是持久节点,则该属性值为 0
    dataLength 数据内容的长度
    numChildren 数据节点当前的子节点个数
    # 3.5 查看节点状态

    ​ 可以使用 stat 命令查看节点状态,它的返回值和 get 命令类似,但不会返回节点数据

    //使用stat命令来查看节点状态
    [zk: localhost:2181(CONNECTED) 0] stat /hadoop
    cZxid = 0x4
    ctime = Thu May 14 07:56:33 CST 2020
    mZxid = 0x4
    mtime = Thu May 14 07:56:33 CST 2020
    pZxid = 0x4
    cversion = 0
    dataVersion = 0
    aclVersion = 0
    ephemeralOwner = 0x0
    dataLength = 6
    numChildren = 0
    [zk: localhost:2181(CONNECTED) 1]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 3.6 查看节点列表

    ​ 查看节点列表有 ls path 和 ls2 path 两个命令,后者是前者的增强。不仅可以查看指定路径下的所有节点,还可以查看当前几点的信息

    [zk: localhost:2181(CONNECTED) 5] ls /
    [a0000000001, hadoop, zookeeper, tmp, hadoop2]
    [zk: localhost:2181(CONNECTED) 6] ls2 /
    [a0000000001, hadoop, zookeeper, tmp, hadoop2]
    cZxid = 0x0
    ctime = Thu Jan 01 08:00:00 CST 1970
    mZxid = 0x0
    mtime = Thu Jan 01 08:00:00 CST 1970
    pZxid = 0xb
    cversion = 3
    dataVersion = 0
    aclVersion = 0
    ephemeralOwner = 0x0
    dataLength = 0
    numChildren = 5
    [zk: localhost:2181(CONNECTED) 7]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 3.7 监听器 get path [watch]

    ​ 使用 get path [watch] 注册的监听器能够在节点内容发生改变的时候,向客户点发出通知。需要注意的是 zookeeper 的触发器是一次性的(One-time trigger),触发一次后就会立即失效。

    [zk: localhost:2181(CONNECTED) 8] get /hadoop watch
    123456
    cZxid = 0x4
    ctime = Thu May 14 07:56:33 CST 2020
    mZxid = 0x4
    mtime = Thu May 14 07:56:33 CST 2020
    pZxid = 0x4
    cversion = 0
    dataVersion = 0
    aclVersion = 0
    ephemeralOwner = 0x0
    dataLength = 6
    numChildren = 0
    [zk: localhost:2181(CONNECTED) 9]
    WATCHER::
    
    WatchedEvent state:SyncConnected type:NodeDataChanged path:/hadoop
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 3.8 监听器 stat path [watch]

    ​ 使用 stat path [watch] 注册的监听器能够在节点抓哪个台发生改变的时候,向客户点发出通知。

    [zk: localhost:2181(CONNECTED) 1] stat /hadoop watch
    cZxid = 0x4
    ctime = Thu May 14 07:56:33 CST 2020
    mZxid = 0xf
    mtime = Thu May 14 10:01:04 CST 2020
    pZxid = 0x4
    cversion = 0
    dataVersion = 1
    aclVersion = 0
    ephemeralOwner = 0x0
    dataLength = 6
    numChildren = 0
    [zk: localhost:2181(CONNECTED) 2]
    WATCHER::
    
    WatchedEvent state:SyncConnected type:NodeDataChanged path:/hadoop
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 4.9 监听器 ls\ls2 path [watch]

    ​ 使用 ls path [watch] 或者 ls2 path [watch] 注册的监听器能够监听该节点下所有子节点的增加和删除操作。

    # 四、zookeeper 权限控制

    # 4.1 概述

    ​ zookeeper 类似于文件系统,client 可以创建节点,更新节点,删除节点,那么如何做到节点的权限的控制呢?zookeeper 的 access control list 访问控制列表可以自耦到这一点。

    ​ acl 权限控制,使用 scheme:id:permission 来表示,主要涵盖 3 个方面:

    • 权限模式(scheme):授权的策略

    • 授权对象(id):授权的对象

    • 权限(permission):授予的权限

      其特性如下:

    • zookeeper 的权限控制是基于每个 znode 节点的,需要对每个节点设置权限

    • 每个 znode 支持设置多种权限控制方案和多个权限

    • 子节点不会继承父节点的权限,客户点无权访问某个节点,但可能可以访问他的子节点

    setAcl /test2 ip:192.168.60.130:rewda //将节点权限设置为Ip:192.168.60.130的客户端可以对节点进行增、删、该、查、权限管理
    
    1
    # 4.2 权限模式

    ​ 采用何种方式授权

    方案 描述
    world 只有一个用户:anyone,代表登录 zooleeper 所有人(默认)
    ip 对客户端使用 ip 地址认证
    auth 使用已添加认证的用户认证
    digest 使用“用户名:密码”方式认证
    # 4.3 授权的对象

    ​ 给谁授权

    ​ 授权对象 ID 是指,权限赋予的实体例如:ip 地址或用户。

    # 4.4 授予的权限

    ​ 授予什么权限

    ​ create、delete、read、writer、admin 也就是增、删、改、查、管理权限,这 5 中权限简写为 cdrwa、注意:这 5 中权限中,delete 是指对子节点的删除权限,其他 4 种权限值对自身节点的权限操作。

    权限 ACL 简写 描述
    create c 可以创建子节点
    delete d 可以删除子节点(仅下一级节点)
    read r 可以读取节点数据及显示子节点列表
    write w 可以设置节点数据
    admin a 可以设置节点访问控制列表权限
    # 4.5 授权的相关命令
    命令 使用方式 描述
    getAcl getAcl<path> 读取 ACL 权限
    setAcl setAcl<path><acl> 设置 ACL 权限
    addauth addauth<scheme><auth> 添加认证用户
    # 4.6 案例
    • world 授权模式:

      命令

      setAcl <path> world:anyone:<acl>
      setAcl /hadoop world:anyone:cdrwa
      
      1
      2

      案例

      [zk: localhost:2181(CONNECTED) 2] getAcl /hadoop
      'world,'anyone   #world方式对所有用户进行授权
      : cdrwa          #增、删、改、查、管理
      [zk: localhost:2181(CONNECTED) 3]
      
      1
      2
      3
      4
    • IP 授权模式

      命令

      #需要两台机器来进行连接 192.168.60.129  192.168.60.130
      #使用 192.168.60.129  登录zookeeper
      zkCli.sh -server 192.168.60.130
      #使用本机  192.168.60.130 zookeeper
      zkCli.sh -server 192.168.60.130
      
      1
      2
      3
      4
      5

      image-20200514201404534

    • Auth 授权模式

      命令

      addauth digest <user>:<password> #添加认证用户
      setAcl <path>auth:<user>:<acl>
      
      1
      2

      案例

    [zk: localhost:2181(CONNECTED) 0] create /hadoop3 "hadoop3"
    Created /hadoop3
    [zk: localhost:2181(CONNECTED) 1] getAcl /hadoop3
    'world,'anyone
    : cdrwa
    [zk: localhost:2181(CONNECTED) 2] addauth digest tyx:123
    [zk: localhost:2181(CONNECTED) 3] setAcl /hadoop3 auth:tyx:cdrwa
    cZxid = 0x16
    ctime = Thu May 14 20:29:34 CST 2020
    mZxid = 0x16
    mtime = Thu May 14 20:29:34 CST 2020
    pZxid = 0x16
    cversion = 0
    dataVersion = 0
    aclVersion = 1
    ephemeralOwner = 0x0
    dataLength = 7
    numChildren = 0
    [zk: localhost:2181(CONNECTED) 7] getAcl /hadoop3
    'digest,'tyx:nSk0WYb+XoISHNhIQiQ1BGsZHjE=
    : cdrwa
    [zk: localhost:2181(CONNECTED) 8]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    • Digest 授权模式

      命令

      setAcl <path> digest:<user>:<password>:<acl>
      
      1

      这里的密码是经过 SHA1 及 BASE64 处理的密文,在 SHEEL 中可以通过命令计算:

      echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64
      
      1

      先来计算一个密文

      echo -n tyx:123 | openssl dgst -binary -sha1 | openssl base64
      #得到的密文
      [root@centos zookeeper-3.4.14]# echo -n tyx:123 | openssl dgst -binary -sha1 | openssl base64
      nSk0WYb+XoISHNhIQiQ1BGsZHjE=
      
      1
      2
      3
      4

      案例:

      
      [zk: localhost:2181(CONNECTED) 8] create /hadoop4 "hadoop4"
      Created /hadoop4
      [zk: localhost:2181(CONNECTED) 10] getAcl /hadoop4
      'world,'anyone
      : cdrwa
      //使用digest进行授权
      [zk: localhost:2181(CONNECTED) 11] setAcl /hadoop4 digest:tyx:nSk0WYb+XoISHNhIQiQ1BGsZHjE=:cdrwa
      
      //该节点的权限
      [zk: localhost:2181(CONNECTED) 13] getAcl /hadoop4
      'digest,'tyx:nSk0WYb+XoISHNhIQiQ1BGsZHjE=
      : cdrwa
      //没有权限
      [zk: localhost:2181(CONNECTED) 0] get /hadoop4
      Authentication is not valid : /hadoop4
      //添加授权用户
      [zk: localhost:2181(CONNECTED) 1] addauth digest tyx:123
      [zk: localhost:2181(CONNECTED) 2] get /hadoop4
      hadoop4
      cZxid = 0x19
      ctime = Thu May 14 21:12:46 CST 2020
      mZxid = 0x19
      mtime = Thu May 14 21:12:46 CST 2020
      pZxid = 0x19
      cversion = 0
      dataVersion = 0
      aclVersion = 1
      ephemeralOwner = 0x0
      dataLength = 7
      numChildren = 0
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
    • 多种密室授权:

      同一个节点可以同时使用多种模式授权

      [zk: localhost:2181(CONNECTED) 8] create /hadoop4 "hadoop4"
      Created /hadoop4
      //添加认证用户
      [zk: localhost:2181(CONNECTED) 8] addauth digest itcast:123456
      [zk: localhost:2181(CONNECTED) 8] setAcl /hadoop4 ip:192.168.60.129:cdrwa,auth:tyx:123:cdrwa,digest:tyx:nSk0WYb+XoISHNhIQiQ1BGsZHjE=:cdrwa
      
      1
      2
      3
      4
      5
    # 4.7 acl 超级管理员

    zookeeper 的权限管理模式有一种叫做 super,该模式提供一个超管,可以方阿斌的访问任何权限的节点

    假设这个超管是:super:admin,需要先为超管生成密码的密文

    echo -n super:admin | openssl dgst -binary -sha1 | openssl base64
    
    1

    那么打开 zookeeper 目录下的/bin/zkServer.sh 服务器脚本,找到如下一行:

    image-20200514212640310

    之后启动 zookeeper,输入如下的命令添加权限

    addauth digest super:admin  #添加认证用户
    
    1

    # 五、zookeeper javaAPI

    ​ znode 是 zookeeper 集合的核心组件,zookeeper API 提供了一小组方法使用 zookeeper 集合来操作 znode 所有的细节。

    客户端用该遵循以下步骤,与 zookeeper 服务器进行清洗和干净的交互。

    • 连接到 zookeeper 服务器。zookeeper 服务器为客户端分配回话 ID。
    • 定期向服务器发送心跳。否则,zookeeper 服务器将过期回话 ID,客户端需要重新连接。
    • 只要回话 ID 处于活动状态,就可以获取/设置 znode。
    • 所有任务完成后,断开与 zookeeper 服务器的连接。如果客户端长时间不活动,则 zookeeper 服务器将自动断开客户端。
    # 5.1 连接到 zookeeper
    Zookeeper(String connectionString,int sessionTimeout,Watcher watcher)
    
    1
    • connectionString -zookeeper 主机
    • sessionTimeout - 回话超时(一毫秒为单位)
    • watcher -实现“监视器”对象。zookeeper 集合通过监视器对象返回连接状态。
    package com.example.springcloud.controller;
    
    import org.apache.zookeeper.WatchedEvent;
    import org.apache.zookeeper.Watcher;
    import org.apache.zookeeper.ZooKeeper;
    
    import java.io.IOException;
    import java.util.concurrent.CountDownLatch;
    
    /**
     * @author papi
     * @data 2020/5/1
     */
    public class ZookeeperTest {
    
        public static void main(String[] args) {
            try {
                CountDownLatch countDownLatch = new CountDownLatch(1);
                //arg1:服务器和端口号
                //arg2: 客户端和服务器之间的会话超时时间
                //arg3: 监视器对象
                ZooKeeper zooKeeper = new ZooKeeper("192.168.90.108:2181", 5000, new Watcher() {
                    @Override
                    public void process(WatchedEvent watchedEvent) {
                        if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                            System.out.println("连接成功");
                            //通知主线不要去等待了
                            countDownLatch.countDown();
                        }
                    }
                });
                //主线程阻塞等待连接对象
                countDownLatch.await();
                //获取zookeeper的session会话编号
                System.out.println(zooKeeper.getSessionId());
                zooKeeper.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    # 5.2 新增节点
    //同步方式
    create(String path, byte[] data, List<Acl> acl, CreateMode createMode)
    //异步方式
    create(String path, byte[] data, List<Acl> acl, CreateMode createMode, AsyncCallback.StringCallback callBack,Object ctx)
    
    1
    2
    3
    4
    • path -znode 路径。例如,/node1 /node1/node11
    • data -要存储的指定 znode 路径中的数据
    • acl - 要创建的节点的访问控制列表。zookeeper API 提供了一个静态接口ZooDefs.Ids 来获取一些基本的 acl 列表。例如,Zoodefs.Ids.OPEN_ACL_USERAFE 返回发开 znode 的 acl 列表。
    • createMode -节点的类型,这是一个枚举。
    • callBack -异步回调接口
    package com.example.springcloud.controller;
    
    import org.apache.zookeeper.*;
    import org.apache.zookeeper.data.ACL;
    import org.apache.zookeeper.data.Id;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    
    /**
     * @author papi
     * @data 2020/5/15
     */
    public class ZookeeperTest {
    
        public static void main(String[] args) {
            try {
                CountDownLatch countDownLatch = new CountDownLatch(1);
                //arg1:服务器和端口号
                //arg2: 客户端和服务器之间的会话超时时间
                //arg3: 监视器对象
                ZooKeeper zooKeeper = new ZooKeeper("192.168.90.108:2181", 5000, new Watcher() {
                    @Override
                    public void process(WatchedEvent watchedEvent) {
                        if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                            System.out.println("连接成功");
                            //通知主线不要去等待了
                            countDownLatch.countDown();
                        }
                    }
                });
                //主线程阻塞等待连接对象
                countDownLatch.await();
                //获取zookeeper的session会话编号
                System.out.println(zooKeeper.getSessionId());
                zooKeeper.create("/hadoop7","hadoop7".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
                //ZooDefs.Ids.READ_ACL_UNSAFE world:anyone:r
                zooKeeper.create("/hadoop8", "hadoop8".getBytes(), ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT );
                //world 模式
                //权限列表
                List<ACL> list = new ArrayList<>();
                Id id = new Id();
                id.setScheme("world");
                id.setId("anyone");
                list.add(new ACL(ZooDefs.Perms.READ, id));
                list.add(new ACL(ZooDefs.Perms.WRITE, id));
                zooKeeper.create("/hadoop9", "hadoop9".getBytes(), list, CreateMode.PERSISTENT );
    
                //IP授权模式
                List<ACL> acls = new ArrayList<>();
                //授权模式和授权对象
                Id id1 = new Id("ip", "192.168.60.130");
                acls.add(new ACL(ZooDefs.Perms.ALL, id1));
                zooKeeper.create("/hadoop10", "hadoop10".getBytes(), acls, CreateMode.PERSISTENT);
    
                //使用auth模式进行授权
                zooKeeper.addAuthInfo("digest","tyx:123".getBytes());
                zooKeeper.create("/hadoop11", "hadoop11".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL,CreateMode.PERSISTENT);
    
                //使用auth模式进行授权,只读的模式
                zooKeeper.addAuthInfo("digest","tyx:123".getBytes());
                List<ACL> acls2 = new ArrayList<>();
                Id id2 = new Id("auth", "tyx");
                acls.add(new ACL(ZooDefs.Perms.READ, id1));
                zooKeeper.create("/hadoop12", "hadoop12".getBytes(), acls2,CreateMode.PERSISTENT);
                //使用digest模式进行授权
                List<ACL> acls3 = new ArrayList<>();
                Id id3 = new Id("digest", "tyx:adkalfjasncasaKJHG=");
                acls.add(new ACL(ZooDefs.Perms.ALL, id3));
                zooKeeper.create("/hadoop13", "hadoop13".getBytes(), acls3,CreateMode.PERSISTENT);
    
                //持久化有序节点
                zooKeeper.create("/hadoop14", "hadoop14".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL,CreateMode.PERSISTENT_SEQUENTIAL);
    
                //临时节点
                zooKeeper.create("/hadoop15", "hadoop15".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL,CreateMode.EPHEMERAL);
                //临时顺序节点
                zooKeeper.create("/hadoop16", "hadoop16".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL,CreateMode.EPHEMERAL_SEQUENTIAL);
    
                //异步创建节点
                zooKeeper.create("/hadoop16", "hadoop16".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT, new AsyncCallback.StringCallback() {
                    @Override
                    public void processResult(int i, String s, Object o, String s1) {
                        //0代表创建成功
                        System.out.println(i);
                        //节点的路径
                        System.out.println(s);
                        //节点的路径
                        System.out.println(o);
                        //上下文参数
                        System.out.println(s1);
                    }
                },"I am context");
                zooKeeper.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    # 5.3 更新节点
    //同步方式
    setData(String path,byte[] data, int version)
    //异步方式
    setData(String path, byte[] data, int version, AsyncCallback.StatCallback callBack, Object ctx)
    
    1
    2
    3
    4
    • path -znode 路径
    • data -要存储在指定 znode 路径中的数据。
    • version -znode 的当前版本号。每当数据更改时,Zookeeper 会更新 znode 的版本号。
    • callBack -异步回调接口
    • ctx 传递上下文参数
    # 5.4 删除节点
    //同步方式
    delete(String path, int version)
    //异步方式
    delete(String path, int version, AsyncCallback.VoidCallback callBack, Object ctx)
    
    1
    2
    3
    4
    • path -znode 路径
    • version -znode 的当前版本
    • callBack -异步回调接口
    • ctx -传递上下文参数

    案例

    //arg1:删除节点的节点路径
    //arg2:数据版本信息 -1代表删除节点时不考虑节点版本信息
    zooKeeper.delete("/delete/node1", -1);
    delete(String path, int version, AsyncCallback.VoidCallback callBack, Object ctx)
    
    1
    2
    3
    4
    # 5.5 查看节点
    //同步方式
    getData(String path, boolean b, Stat stat)
    //异步方式
    getData(String path, boolean b, AsyncCallback.DataCallback callBack, Object ctx)
    
    1
    2
    3
    4
    • path -znode 路径
    • b -是否使用连接对象中注册的监视器。
    • stat -返回 znode 的元数据。
    • callBack -异步回调接口
    • ctx -传递上下文参数

    image-20200517084541194

    //异步方式获取

    image-20200517084851983

    # 5.6 查看子节点
    //同步方式
    getChildren(String path, boolean b)
    //异步方式
    getChildren(String path, boolean b, AsyncCallback.ChildrenCallback callBack, Object ctx)
    
    1
    2
    3
    4
    • path -Znode 路径。
    • b -是使用连接对象中注册的监视器。
    • callBack -异步回调接口。
    • ctx -传递上下文参数。
    //同步方式
    List<String> list = zookeeper.getChildren("/get",false);
    for(String str : list){
        System.out.println(str);
    }
    
    //异步方式
    zookeeper.getChildren("/get", false, new AsyncCallback.ChildrenCallback(){
        @Override
        public void processResult(int rc, String path,Object ctx, List<String> children){
            //0代表读取成功
            System.out.println(rc);
            //子节点路径
            System.out.println(path);
            //上下文参数对象
            System.out.println(ctx);
            //子节点信息
            for(String str : children){
        		System.out.println(str);
    		}
        }
    },"I am Context")
     Thread.sleep(1000);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # 6.7 检查节点是否存在
    //同步方法
    exists(String path, boolean b);
    //异步方法
    exists(String path, boolean b, AsyncCallback.StatCallBack, Object ctx)
    
    1
    2
    3
    4
    • path -znode 路径
    • b -是否使用连接对象中注册的监视器
    • callBack -异步回调接口
    • ctx -上下文参数

    # 六、zookeeper 事件监听机制

    # 6.1 watcher 概念

    ​ zookeeper 提供了数据的发布/订阅功能,对个订阅者可同时监听某一特定主题对象,当该主题对象的自身状态发生变化时(例如节点内容改变,节点下的子节点列表改变等),会实时,主动通知所有订阅者;

    ​ zookeeper 采用了 watcher 机制实现数据的发布/订阅功能。该机制在被订阅对象发生变化时会异步通知客户端,因此客户端不必在 Watcher 注册后轮询阻塞,从而减轻了客户点压力。

    ​ watcher 机制实际上与观察者密室类似,也可以看作是一种观察者密室在分布式场景下的实现方式。

    # 6.2 wathcer 架构

    ​ Watcher 实现由三个部分组成:

    • Zookeeper 服务端
    • Zookeeper 客户端
    • 客户端的 ZKWatchManager 对象

    ​ 客户端首先将 Watcher 注册到服务端,同时将 Watcher 对象保存到客户端的 Watch 管理器中。当 Zookeeper 服务端监听的数据状态发生变化时,服务端会主动同时客户端,接着客户端的 Watch 管理器会触发相关 Watcher 来回调相应处理逻辑,从而完成整体的数据发布/订阅流程。

    image-20200517223229316

    # 6.3 wahcher 特性
    特性 说明
    一次性 wathcer 是一次性的,一旦被触发就会移除,再次使用时需要重新注册
    客户端顺序回调 watcher 回调是顺序串行化执行的,只有回调后客户端才能看到最新的数据状态。一个 watcher 回调逻辑不用太多,以免影响别的 watcher 执行
    轻量级 wathcerEvent 是最小的通信单元,结构上只包含通知状态,事件类型和节点路径,并不会告诉数据节点变化前后的具体内容;
    时效性 watcher 只有在当前 session 彻底失效时才会无效,若 session 有效期内快速重连成功。则 wacher 依然存在,然可接收到通知;
    # 6.4 watcher 接口设计
    • Wacher 通知状态

    ​ KeeperState 是客户端与服务 daunt 连接状态发生变化时对应的通知类型。路径为 org.apache.zookeeper.Watcher.Event.KeeperState,是一个枚举类,其枚举属性如下:

    枚举属性 说明
    SyncConnected 客户端与服务器正常连接时
    Disconnected 客户端与服务器断开连接时
    Expired 回话 session 失效时
    AuthFailed 身份认证失败时
    • Watcher 事件类型(EventType)

      EventType 是数据节点(znode)发生变化时对应的通知类型。EventType 变化时 KeeperState 永远处于 SyncConnected 通知状态下;当 KeeperState 发生变化时,EventType 永远为 None。其路径为 org.apache.zookeeper.Watcher.Event.EventType,是一个枚举类,枚举类属性如下:

    枚举属性 说明
    None 无
    NodeCreated Watcher 监听的数据节点被创建时
    NodeDeleted Watcher 监听的数据节点被删除时
    NodeDataChanged Watcher 监听的数据节点内容发生变更时(无论内容数据是否变化)
    NodeChildrenChanged Watcher 监听的数据节点的子节点列表发生变更时
    # 6.5 捕获相应的事件

    ​ 上面讲到的 zookeeper 客户端连接的状态和 zookeeper 对 znode 节点监听的事件类型,下面我们来将如何建立 zookeeper 的 watcher 监听。在 zookeeper 中采用 zk.getChildren(path,watch)、zk.exists(path,watch),zk.getData(path,watcher,stat)这样的方式为某个 znode 注册监听。

    下表以 node-x 节点为例,说明调用的注册方法和监听事件间的关系:

    注册方式 Created ChildrenChanged Changed Deleted
    zk.exists("/node-x",watcher) 可监控 可监控 可监控
    zk.getData("/node-x",watcher) 可监控 可监控
    zk.getChildren("/node-x",watcher) 可监控 可监控
    # 6.5 注册 watcher 的方法

    客户端与服务器的连接状态

    KeeperState : 通知状态
    SyncConnected:客户端与服务器正常连接时
    Disconnected:客户端与服务器断开连接时
    Expired:回话session失效时
    AuthFailed:身份认证失败时
    
    事件类型为:None
    
    1
    2
    3
    4
    5
    6
    7
    # 6.6 查看节点
    //使用连接对象的监听器
    getData(String path, boolean b, Stat stat)
    //滴定仪监视器
    getData(String path, boolean b, Stat stat)
    //NodeDeleted:节点删除
    //NodeDataChanged:节点内容发生变化
    
    1
    2
    3
    4
    5
    6
    • path -znode 路径。
    • b -是否使用连接对象中注册的监听器。
    • w -监视器对象。
    • stat - 返回 znode 的元数据

    # 七、配置中心案例

    ​ 工作中有这样一个场景:数据库用户名和密码信息放在一个配置文件中,应用读取该配置文件,配置文件信息放入缓存。

    ​ 若数据库的用户名和密码改变时候,还需要重新加载缓存,比较麻烦,通过 Zookeeper 可以轻松完成,当数据库发生变化时自动完成缓存同步。

    设计思路:

    1. 连接 zookeeper 服务器
    2. 读取 zookeeper 中的配置信息,注册 watcher 监听器,存入本地变量
    3. 当 zookeeper 中的配置信息发生变化时,通过 watcher 的回调方法捕获数据变化事件。
    4. 重新获取配置信息。

    # 八、生成分布式唯一 ID

    ​ 在过去的单库单表型系统中,通常可以使用数据库字段自带的 auto_increment 属性来自动为每条记录生成一个唯一的 ID。但是分库分表后,就无法再依靠数据库的 auto_increment 属性唯一标识一条记录了。此时我们就可以用 zookeeper 在分布式环境下生成全局唯一 ID。

    设计思路:

    1. 连接 zookeeper 服务器
    2. 指定路径下生成临时有序节点
    3. 取序号及为分布式环境下的唯一 ID

    # 九、分布式锁

    ​ 分布式锁有多重实现方式,比如通过数据库,redis 都可以实现。作为分布式协同工具 Zookeeper,当然也有者标准的实现方式。下面介绍在 zookeeper 中如何实现排它锁。

    设计思路:

    1、每个客户端往/Locks 下创建临时有序节点/Locks/Lock_,创建成功后/Locks 下面会有每个客户端对应的节点。如/Locks/Lock_0000001

    2、客户端取得/Locks 下子节点,并进行排序,判断排在最前面的是否为自己,如果自己的锁节点在第一位,代表获取锁成功。

    3、如果自己的锁节点不在第一位,则监听自己前一位的锁节点。如果自己锁节点 Lock_000002,那么则监听 Lock_0000001

    4、当前一位锁节点(Lock_000000001)对应的客户端执行完成,释放了锁,将会触发监听客户端(Lock_000002)的逻辑

    5、监听客户端重新执行第 2 步逻辑,判断自己是否获得了锁;

    # 十、zookeeper 集群搭建

    ​ 单机环境下,jdk、zookeeper 安装完毕,基于一台虚拟机,进行 zookeeper 伪集群搭建,zookeeper 集群中包含 3 个节点,节点对外提供服务端口号分别为 2181,2182,2183

    # 10.1 配置 zookeeper 服务器文件

    ​ 1、基于 zookeeper-3.4.14 复制三分 zookeeper 安装好的服务器文件,目录名称分别为 zookeeper2181、zookeeper2182、zookeeper2183

    cp -r zookeeper-3.4.14 zookeeper2181
    cp -r zookeeper-3.4.14 zookeeper2182
    cp -r zookeeper-3.4.14 zookeeper2183
    
    1
    2
    3

    2.修改 zookeeper2181 服务器对应配置文件

    #服务器对应端口号
    clientPort=2181
    #数据快照文件所在路径
    dataDir=/home/zookeeper/zookeeper2181/data
    #集群配置信息
    	#server.A=B:C:D
    	#A:是一个数字,表示这个是服务器的编号
    	#B:是这个服务器的ip地址
    	#C:Zookeeper服务器之间的通信端口号
    	#D:Leader选举的端口
    server.1=192.168.60.130:2287:3387
    server.1=192.168.60.130:2288:3388
    server.1=192.168.60.130:2289:3389
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    1. 在上一步 dataDir 指定的目录下,创建 myid 文件,然后在该文件添加上一步 server 配置对应 A 数字。
    #zookeeper2181对应的数字为1
    #/home/zookeeper/zookeeper2181/data目录下执行命令
    echo "1" > myid
    
    1
    2
    3

    4.zookeeper2182 、zookeeper2183 参照 2,3 不找进行相应配置

    5.分别启动三台服务器,检验集群状态

    ​ 登录命令:

    ./zkCli.sh -server 192.168.60.130:2181
    ./zkCli.sh -server 192.168.60.130:2182
    ./zkCli.sh -server 192.168.60.130:2183
    
    1
    2
    3
    # 10.2 一致性协议:zab 协议

    ​ zab 协议的全称是Zookeeper Atomic Broadcast (zookeeper 原子广播)。zookeeper 是通过 zab 协议来保证分布式事务的最终一致性

    ​ 基于 zab 协议,zookeeper 集群中的角色主要有一下三类,如下表所示:

    角色 描述
    领导者(leader) 领导者负责进行投票的发起和决议,更新系统状态
    学习者(Learner) Follower 用于接受客户请求并向客户端返回结果,在选主过程中参与投票
    ObServer 可以接受客户端连接,将写请求转发给 leader 节点。但是 ObServer 不参加投票过程,只同步 leader 的状态。ObServer 的目的是为了扩展系统,提高读取速度
    客户端(Client) 请求发起方。

    zab 广播模式工作原理,通过类似两阶段提交协议的方式解决数据一致性:

    image-20200525002051091

    1. leader 从客户端收到一个写请求
    2. leader 生成一个新的事务并未这个事务生成一个唯一的 ZXID
    3. leader 将这个事务提议(propose)发送给所有的 follows 节点
    4. follower 节点将收到的事务请求加入到历史队列(history queue)中,并发送 ack 给 leader
    5. 当 leader 收到大多数 follower(半数以上节点)的 ack 消息,leader 会发送 commit 请求
    6. 当 follower 收到 commit 请求时,从历史队列中将事务请求 commit
    # 10.3 zookeeper 的 leader 选举

    ​ 1、服务器状态

    ​ looking:寻找 leader 状态。当服务器处于该状态时,它会认为当前集群中没有 leader,因此需要进入 leader 选举状态。

    ​ leading:领导者状态。表明当前服务器角色是 leader。

    following:跟随者状态。表明当前服务器角色是 follower。

    ​ observing:观察者状态。表明当前服务器角色是 observer。

    2、服务器启动时期的 leader 选举

    ​ 在集群初始化阶段,当有一台服务器 server 启动时,其单独无法进行完成 leader 选举,当第二太服务器 server2 启动时,此时两台机器可以相互通信,每台机器都视图 zhaodaoleader。于是进入 leader 选举过程。选举过程如下:

    1. 每个 server 发出一个投票。由于是初始情况,server1 和 server2 都会将自己作为 leader 服务器进行投票,没投票会包含所推举的服务器的 myid 和 zxid,使用(myid,zxid(事务 ID))来表示,此时 server1 的投票为(1,0),server2 的投票为(2,0),然后各自将这个投票发给集群中其他机器。
    2. 集群中的每一台服务器接受来自集群中各个服务器的投票。
    3. 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行 pk,pk 规则如下
      • 优先检查 zxid(事务的编号)。zxid 比较大的服务器优先为 leader。
      • 如果 zxid 相同,那么就比较 myid(服务器的编号)。myid 比较大的服务器作为 leader 服务器。

    ​ 对于 Server1 而言,它的投票是(1,0),接受 Server2 的投票为(2,0),首先会比较两者的 zxid,均为 0,在比较 myid,此时 server2 的 myid 最大,于是更新自己的投票为(2,0),然后重新投票,对于 server2 而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次的投票信息即可。

    1. 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于 server1、server2 而言,都统计出集群中已经有两台机器接受了(2,0)的投票信息,此时便认为已经选出了 leader。
    2. 改变服务状态。一旦确定了 leader,每个服务器就会更新自己的状态,如果是 follower,那么久变更为 following,如果是 leader,就变更为 leading。
    # 10.4 服务器运行时期的 Leader 选举【zxid:事务 ID】

    ​ 在 zookeeper 运行期间,leader 与非 leader 服务器各司其职,即便当有非 leader 服务器宕机或新加入,此时也不会影响 leader,但是一旦 leader 服务器挂了,那么这个集群将暂停对外服务,进入新一轮 leader 选举,其过程和启动时期的 Leader 选举过程基本一致。

    ​ 假设正在运行的 server1、server2、server3 三台服务器,当前 leader 是 server2,若某一时刻 leader 挂了、此时开始 Leader 选举。选举过程如下:

    1. 变更状态。leader 挂后,剩余的服务器都会将自己的服务状态变更为 looking,然后开始进入 leader 选举过程。
    2. 每个 server 会发出一个投票。在运行期间,每个服务器上的 zxid 可能不同,此时嘉定 server1 的 ZXID 为 122,server3 的 zxid 为 122,在第一轮投票中,server1 和 server3 都会投自己、产生投票(1,122),(3,122),然后各自将投票发送给集群中的所有机器。
    3. 接受来自各个服务器的投票。与启动时过程相同。
    4. 处理投票。与启动时过程相同,此时,server3 将会成为 leader。
    5. 统计投票。与启动时过程相同。
    6. 改变服务器的状态。与启动时过程相同。
    # 10.5 observer 角色及其配置

    ​ observer 角色特点:

    1. 不参与集群的 leader 选举

    2. 不参与集群中写数据时的 ack 反馈

      为了使用 observer 角色,在任何想变成 observer 角色的配置文件中加入如下配置:

    peerType=observer
    
    1

    ​ 并在所有的 server 的配置文件中,配置成 observer 模式的 server 的哪行配置追加:observer,例如:

    server.3=192.168.60.130:2289:3389:observer
    
    1
    # 10.6 zookeeperAPI 连接集群
    Zookeeper(String connectionString, int sessionTimeout, Watcher watcher)
    
    1
    • **connectionString ** -zooKeeper 集合主机
    • sessionTimeout - 回话超时(以毫秒为单位)。
    • watcher - 实现“监视器”界面的对象。Zookeeper 集合通过监视器对象返回连接状态。

    # 十一、zookeeper 开源客户端 curator 介绍

    • zookeeper 开源客户端 curator 介绍
    • zookeeper 四字监控命令
    • zookeeper 图形化的客户端工具(Zoolnspector)
    • taokeeper 监控工具的使用
    # 11.1 curator 简介

    ​ curator 是 Netflix 公司开源的一个 zookeeper 客户端,后捐赠给 apache,curator 框架在走哦科普日原生 API 接口上进行了包装,解决了很多 zookeeper 客户端非底层的细节开发。提供了 zookeeper 各种应用场景(比如:分布式锁服务,集群领导选举,共享计数器,缓存机制,分布式队列等)的抽象封装,实现了 Fluent 风格的 API 接口,是最好用,最流行的 zookeeper 的客户端。

    ​ 原生 zookeeperAPI 的不足:

    • 连接对象异步创建,需要开发人员自行编码等待。

    • 连接没有自动重连超时机制

    • watcher 一次注册生效一次

    • 不支持递归创建树形节点

      curator 特点:

    • 解决 session 会话超时重连

    • watche 反复注册

    • 简化开发 api

    • 遵循 Fluent 风格的 API

    • 提供了分布式锁服务,共享计数器,缓存机制等机制

    # 11.2 zookeeoer 四字监控命令

    ​ zookeeper 支持某些特定的四字命令与其他的交互。他们大多数是查询命令,用来获取 zookeeper 服务的当前状态及相关的信息。用户在客户点可以通过 telnet 或 nc 向 zookeeper 提交相应的命令。走哦可额偶尔常用的四字命令见下表:

    命令 描述
    conf 输出相关服务的配置的详细信息。比如端口,zk 数据及日志配置路径,最大连接数,session 超时时间,serverid 等。
    cons 列出所有连接到台服务器的客户端连接/回话的详细信息。包括“接受/发送”的包数量、session id。操作延迟,最后的操作执行等信息。
    crst 重置当前这台服务器所有连接/回话的统计信息
    envi 输出关于服务器的环境详细信息
    ruok 测试服务器的详细信息:接受/发送包数量、连接数、模式(leader/follower)、节点总数、延迟、所有客户端的列表。
    srst 重置 server 状态
    wchs 累出服务器 watches 的简洁信息:连接总数、watching 节点总数和 watches 总数
    wchc 通过 session 分组,累出 watch 的所有节点,它的输出是一个与 watch 相关的回话的节点列表
    mntr 雷虎集群的健康状态。包括“接受/发送”的包数量,操作延迟,当前服务模式(leader/follower)、节点总数,watch 总数,临时节点总数
    # 11.3 conf 命令

    ​ conf:输出相关服务配置的详细信息

    shell终端输入:echo conf | nc localhost 2181
    
    1
    属性 含义
    clientPort 客户端端口号
    dataDir 数据快照文件目录 默认情况下 100000 次事务操作生成一次快照
    dataLogDir 事务日志文件目录,生产环境中放在独立的磁盘上
    tickTime 服务器之间或客户端与服务器至今维持心跳的时间间隔(以毫秒为单位)
    maxClientCnxns 最大连接数
    minSessionTimeout 最小 session 超时 minSessionTimeout=tickTime*2
    maxSessionTimeout 最大 session 超时 maxSessionTimeout=tickTime*20
    serverId 服务器编号
    initLimit 集群中的 follower 服务器(F)与 leader 服务器(L)之间初始连接时能容忍的最多心跳数
    syncLimit 集群中的 follower 服务器(F)与 leader 服务器(L)之间请求和答应之间能容忍的最多心跳数
    electionAlg 0:基于 UDP 的 LeaderElection 1:基于 UDP 的 FastLeaderElection 2:基于 UDP 和认证的 FastLeaderElection 3:基于 TCP 的 FastLeaderElection 在 3.4.10 版本中,默认值为 3,另外的三种算法已经被弃用了,并且有计划在之后的版本中将它们彻底铲除而不再支持。
    electionPort 选举端口
    quorumPort 数据通信端口
    peerType 是否为观察者 1 为观察者
    # 11.4 cons 命令

    cons:列出所有连接到这台服务器的客户端连接/回话的详细信息

    echo cons | nc localhost 2181
    
    1
    属性 含义
    ip IP 地址
    port 端口号
    queued 等待被处理的请求数,请求缓存在队列中
    received 收到的包数
    sent 发送的包数
    sid 回话 id
    lop 最后的操作 GETD-读取数据 DELE-删除 CREA-创建数据
    est 连接时间戳
    to 超时时间
    lcxid 当前回话的操作 id
    lzxid 最大事务 id
    llat 最后/最新 延时
    minlat 最小延时
    maxlat 最大延时
    avglat 平均延时
    # 11.5 crst 命令

    crst:重置当前这台服务器所有连接/绘画的统计信息

    shell 终端输入:echo crest | nc localhost 2181

    # 11.6 dump 命令

    dump:列出未经处理的回话和临时节点

    shell 终端输入:echo dump | nc loalhost 21

    属性 含义
    session id znode path(1 对多,处于队列中排队的 session 和临时节点)
    # 11.7 eniv 命令

    envi:输出关于服务器的环境详细信息

    shell 终端输入:echo envi |nc localhost 2181

    属性 含义
    zookeeper.version 版本
    host.name host 信息
    java.version java 版本
    java.vendor 供应商
    java.home 运行环境所在目录
    java.class.path classpath
    java.library.path 第三方库指定非 java 类包的位置(如:dll,so)
    java.io.tmpdir 默认的临时文件路径
    java.compiler JIL 编译器的名称
    os.name Linux
    os.arch amd64
    os.version XXXXXXX
    user.name zookeeper
    user.home /home/zookeeper
    user.dir /home/zookeeper/zookeeper2181/bin
    # 11.8 ruok 命令

    ruok:测试服务是否处于正确运行状态

    echo ruok | nv 192.168.60.120 2181
    
    1

    image-20200621234818012

    # 11.9 stat 命令

    stat:输出服务器的详细信息与 srvr 相似,但是多了每个连接的回话信息

    shell 终端输入:echo stat | nc localhost 2181

    属性 含义
    Zookeeper version 版本
    Latency min/avg/max 延时
    Received 收包
    Sent 发包
    Connections 连接数
    OutStanding 堆积数
    Zxid 最大事务 id
    Mode 服务器角色
    Node count 节点数
    # 11.10 srst 命令

    srst:重置 server 状态

    shell终端输入:schoolsrst | nc localhost 2181
    
    1
    # 11.11 wchs 命令

    wchs:累出服务器 watches 的简洁信息

    shell终端输入:echo wchs | nc localhost 2181
    
    1
    属性 含义
    connectsions 连接数
    watch-paths watch 节点数
    watchers watcher 数量
    # 11.12 wchc 命令

    wchc:通过 session 分组,累出 watch 的所有节点,它的输出的是一个与 watch 相关的回话的节点列表问题:

    问题:

    wchc is not executed because it is not in the whiltelist
    
    1

    解决方法:

    #修改启动指令 zkServer.sh
    #注意找到这个信息
    else
      echo "JMX disabled by user request" > & 2
      ZOOMAIN = "org.apache.zookeeper.server.quorum.QuorumPeerMain"
    fi
    
    # 添加如下信息
    ZOOMAIN = "-Dzookeeper.4lw.commands.whitelist=*${ZOOMAIN}"
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    shell 终端输入:echo wchc | nc localhost 2181

    # 11.13 wchp 命令

    查看服务上有多少个节点;

    wchp:通过路径分组,列出所有的 watch 的 session id 信息

    问题:

    wchp is not executer beacues it is not in the whitelist
    
    1

    解决方法:

    
    #修改启动指令 zkServer.sh
    #注意找到这个信息
    else
      echo "JMX disabled by user request" > & 2
      ZOOMAIN = "org.apache.zookeeper.server.quorum.QuorumPeerMain"
    fi
    
    # 添加如下信息
    ZOOMAIN = "-Dzookeeper.4lw.commands.whitelist=*${ZOOMAIN}"
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    shell 终端输入:echo wchp | nc localhost 2181

    # 11.14 mntr 命令

    mntr:猎虎服务器的健康状态

    属性 含义
    zk_version 版本
    zk_avg_latency 平均延时
    zk_max_latency 最大延时
    zk_min_latency 最小延时
    zk_packets_received 收包数
    zk_num_alive_connections 连接数
    zk_outstanding_requests 堆积请求数
    zk_znode_count znode 数量
    zk_watch_count watch 数量
    zk_ephemreals_count 临时节点
    zk_approximate_data_size 数据大小
    zk_open_file_decriptor_count 打开的文件描述符数量
    zk_max_file_descriptor_count 最大文件描述数量
    zk_server_state leader/follower 数量

    shell 终端输入:echo mntr | nc localhost 2181

    上次更新: 2024-10-30 07:54:26

    ← Kafka教程 Nginx→

    Copyright © 2020-2025 wuzhiyong
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式