分布式协调框架——Zookeeper概述、安装、数据类型、Shell客户端操作与JavaAPI操作

Zookeeper

1.Zookeeper概述
  • Zookeeper是一个开源的分布式协调服务框架,主要用来解决分布式集群中应用系统的一致性问题和数据管理问题
2.Zookeeper特点
  • Zookeeper本质上是一个分布式文件系统,也可以理解为一个数据库
  • Zookeeper中存储的其实是一个又一个Znode,Znode是Zookeeper中的节点
    • Znode是有路径的,例如/data/host1,这个路径也可以理解为是Znode中的Name
    • Znode也可以携带数据,例如某个Znode的路径是/data/host1,其值是一个字符串“192.168.0.1”
  • 正因为Znode的特性,所以Zookeeper可以对外提供一个类似于文件系统的视图,可以通过操作文件系统的方式操作Zookeeper
    • 使用路径获取Znode
    • 获取Znode携带的数据
    • 修改Znode携带的数据
    • 删除Znode
    • 添加Znode
3.Zookeeper架构

Zookeeper集群是一个基于主从架构的高可用集群

角色描述
领导者(Leader)集群工作的核心(每个集群同一时间只有一个);集群内部各个服务器的调度者;负责进行投票选举;处理事务性(写操作)请求;参与集群投票
跟随者(Follower)接收客户端请求,并向客户端返回结果;处理非事务性(读操作)请求;转发事务给Leader;参与集群投票
观察者(Observer)接收客户端请求,并向客户端返回结果;处理非事务性(读操作)请求;转发事务给Leader;不参与集群投票
客户端(Client)请求发起方
4.Zookeeper应用场景
  1. 数据发布/订阅
  2. 命名服务
  3. 分布式协调/通知
    • 心跳检测
    • 工作进度汇报
    • 系统调度
  4. 分布式锁
  5. 分布式队列
5.Zookeeper的选举机制

Leader选举是保证分布式数据一致性的关键所在。当Zookeeper集群中的一台服务器出现以下两种情况之一时,需要进入Leader选举

  1. 服务器启动时期

    ① 每个Server发出一个投票,由于是初始情况,Server1和Server2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举服务器的myid和ZXID,使用(myid,ZXID)表示,此时Server1的投票为(1,0),Server2的投票为(2,0),然后各自将这个投票发给集群中的其他机器。

    ② 接受来自各个服务器的投票。集群中每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器。

    ③ 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下:

    • 优先检查ZXID。ZXID比较大的服务器优先作为Leader
    • 如果ZXID相同,那么就比较myid,myid较大的服务器作为Leader。

    myid大的,更新自己的投票。如Server1更新自己的投票为(2,0),Server2不动。

    ④ 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器收到相同的投票信息,对于Server1和Server2而言,都统计出集群中已经有两台机器接收到(2,0)的投票信息,此时便认为已经选出了Leader

    ⑤ 改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态(FOLLOWING和LEADING)

  2. 服务器运行时期的Leader选举

    正常运行期间新加入的机器或非Leader机器并不会对Leader产生影响。若Leader服务器挂掉,则集群暂停对外服务,进行新一轮选举,过程与启动时期类似。

6.Zookeeper安装

集群规划

服务器IP主机名myid
192.168.217.100node011
192.168.217.110node022
192.168.217.120node033

第一步:下载Zookeeper压缩包

http://archive.apache.org/dist/zookeeper/

第二步:上传并解压

[root@node01 zookeeper-3.4.9]# tar zvxf ./zookeeper-3.4.9.tar.gz -C /export/servers/

第三步:修改配置文件

第一台机器修改配置文件

[root@node01 conf]# cd /export/servers/zookeeper-3.4.9/conf/
[root@node01 conf]# cp zoo_sample.cfg zoo.cfg
[root@node01 conf]# mkdir -p /export/servers/zookeeper-3.4.9/zkdatas/
[root@node01 conf]# vim zoo.cfg 

修改以下内容

dataDir=/export/servers/zookeeper-3.4.9/zkdatas
# The number of snapshots to retain in dataDir
#保留多少个快照
autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#日志多少小时清理一次
autopurge.purgeInterval=1
#集群中的服务器地址
server.1=node01:2888:3888
server.2=node02:2888:3888
server.3=node03:2888:3888

第四步:添加myid配置

/export/servers/zookeeper-3.4.9/zkdatas/ 下创建一个文件,文件名为myid,文件内容为1

[root@node01 conf]# echo 1 > /export/servers/zookeeper-3.4.9/zkdatas/myid

第五步:将zookeeper-3.4.9远程拷贝到node02和node03

[root@node01 zkdatas]# scp -r /export/servers/zookeeper-3.4.9/ node02:/export/servers/
[root@node01 zkdatas]# scp -r /export/servers/zookeeper-3.4.9/ node03:/export/servers/

再将node02和node03中的myid内容分别修改为2和3即可。

第六步:启动Zookeeper

在三台机器上分别执行bin/zkServer.sh start

[root@node01 zookeeper-3.4.9]# bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /export/servers/zookeeper-3.4.9/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@node01 zookeeper-3.4.9]# jps
7761 QuorumPeerMain
7789 Jps

使用jps命令出现QuorumPeerMain进程即启动成功

查看状态

[root@node01 zookeeper-3.4.9]# bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /export/servers/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: follower
[root@node02 zookeeper-3.4.9]# bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /export/servers/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: leader
[root@node03 zookeeper-3.4.9]# bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /export/servers/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: follower
7.Zookeeper数据模型
  • Zookeeper的数据模型,在结构上和标准文件的系统非常相似,拥有一个层次的命名空间,都是采用树形层次结构。

  • Zookeeper树中每个节点被称为一个Znode,和文件系统的目录树一样,Zookeeper树中的每个节点可以拥有子节点。

不同之处:

  1. Znode兼具文件和目录两种特点。
  2. Znode存储文件大小有限制。Zookeeper用来管理和调度数据,比如分布式应用中的配置文件信息、状态信息、汇集信息等等。一个Znode一般不超过1M
  3. Znode通过路径引用。路径必须是绝对的、唯一的
  4. 每个Znode由三部分组成:
    • stat:状态信息,描述Znode的版本,权限等信息
    • data:与Znode关联的数据
    • children:该Znode下的子节点
8.Znode节点类型

Znode节点有两种,分别为临时节点和永久节点。节点的类型在创建时被确定,并且不能改变。

  • 临时节点:该节点的生命周期依赖于创建他们的会话。一旦会话结束,临时节点将被自动删除,当然也可以手动删除。临时节点不允许有子节点。
  • 永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。

Znode还有一个序列化的特性。如果创建时指定的话,该Znode后会自动追加一个序列号,类似一个编号。该序列号不断增长,即先创建的大于后创建的。序列号对于此父节点是唯一的,这样便于记录每个节点创建的先后顺序。格式为“%10d”,例如0000000001.

这样便存在四种类型节点:

  • PERSISTENT
  • EPHEMRAL
  • PERSISTENT_SEQUENTIAL
  • EPHEMERAL_SEQUENTIAL
9.Zookeeper的Shell客户端操作
  1. 登录Zookeeper客户端

    [root@node01 zookeeper-3.4.9]# bin/zkCli.sh -server node01:2181 #端口号在zoo.cnf中指定 或
    [root@node01 zookeeper-3.4.9]# bin/zkCli.sh #登录本地的Zookeeper
    
    命令说明参数
    create [-s] [-e] path data acl创建Znode-s指定顺序节点,-e指定临时节点
    ls path [watch]列出path下所有子Znode
    get path [watch]获取path对应Znode的数据和属性
    ls2 path [watch]查看path下所有子Znode以及子Znode的属性
    set path data [version]更新节点数据version数据版本
    delete path [version]删除节点,如果有子Znode无法删除version数据版本
    rmr path递归删除节点
    setquota -n | -b val path修改Znode配额-n设置子节点最大个数,-b设置节点数据
  2. 操作实例

    列出“/”下所有Znode

    [zk: node01:2181(CONNECTED) 0] ls /
    [zookeeper]
    

    创建永久节点hello,数据为world

    [zk: node01:2181(CONNECTED) 1] create /hello world
    

    创建临时节点

    [zk: node01:2181(CONNECTED) 8] create -e /tmp world
    

    创建永久序列化节点

    [zk: node01:2181(CONNECTED) 9] create -s /zhangsan boy  
    Created /zhangsan0000000003
    

    创建临时序列化节点

    [zk: node01:2181(CONNECTED) 10] create -s -e /lisi girl
    Created /lisi0000000004
    

    修改、获取节点数据

    [zk: node01:2181(CONNECTED) 1] set /hello hello
    [zk: node01:2181(CONNECTED) 2] get /hello
    

    删除节点

    [zk: node01:2181(CONNECTED) 3] delete /zhangsan0000000003 #无子节点,可以删除
    [zk: node01:2181(CONNECTED) 14] create /hello/aaa wangbin
    [zk: node01:2181(CONNECTED) 17] delete /hello  #有子节点,无法删除
    Node not empty: /hello
    [zk: node01:2181(CONNECTED) 18] rmr /hello #递归删除
    

    查看历史

    [zk: node01:2181(CONNECTED) 19] history
    
  3. 节点属性

    每个Znode都包含了一系列的属性,通过get获取

    dataVersion:数据版本号,每次set操作dataVersion加1(即使数据没变),可有效避免数据更新时出现先后顺序问题。

    cversion:子节点版本号

    aclVersion:ACL版本号

    cZxid:Znode创建的事务ID

    mZxid:Znode被修改的事务ID,即每次对Znode的修改都会更新

    ctime:节点创建时的时间戳

    mtime:节点最新一次更新时的时间戳

    ephemeralOwner:如果该节点为临时节点,ephemeralOwner表示与该节点绑定的session id,如果不是则为0。

  4. watch机制

    • 类似于数据库中的触发器,对某个Znode设置Watcher,当Znode发生变化的时候,WatchManager会调用对应的Watcher
    • 当Znode发生删除,修改,创建,子节点修改的时候,对应的Watcher会得到通知
    • Watcher的特点
      • 一次性触发。一个Watcher只能被触发一次,如果需要继续监听,则需要再次添加Watcher
      • 事件封装。Watcher得到的事件是被封装过的,包括三个内容:keeperState,eventType,path
    keeperStateeventType触发条件说明
    None连接成功
    SyncConnectedNodeCreatedZnode被创建此时处于连接状态
    SyncConnectedNodeDeletedZnode被删除此时处于连接状态
    SyncConnectedNodeDataChangedZnode数据被改变此时处于连接状态
    SyncConnectedNodeChildChangedZnode的子Znode数据被改变此时处于连接状态
    DisconnectedNone客户端与服务器断开连接此时服务器与客户端端口连接状态
    ExpiredNone会话超时会收到一个SessionExpiredException
    AuthFailedNone权限验证失败会收到一个AuthFailedException
10.Zookeeper的JavaAPI操作

这里操作Zookeeper的JavaAPI使用的是一套Zookeeper客户端框架Curator,解决了很多Zookeeper客户端非常底层的细节开发工作。

Curator包含几个包:

  • curator-framework:对Zookeeper的底层api的一些封装
  • curator-recipes:封装了一些高级特性,如Cache事件监听、选举、分布式锁、分布式计算器等

Maven依赖(Curator版本:2.12.0,对应Zookeeper的版本为3.4.x,跨版本可能会有兼容性问题)

创建Maven工程,导入jar包

<dependencies>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-framework</artifactId>
        <version>2.12.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-recipes</artifactId>
        <version>2.12.0</version>
    </dependency>
    <dependency>
        <groupId>com.google.collections</groupId>
        <artifactId>google-collections</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.25</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.2</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

创建永久节点

package com.wangbin.zookeeper_api;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.junit.Test;

public class ZooKeeperAPITest {
    @Test
    public void createZnode() throws Exception {
        //1.定制重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1); //重试的时间间隔为1000ms,重试次数为1
        //2.获取客户端对象
        String connectionStr = "192.168.217.100:2181,192.168.217.110:2181,192.168.217.120:2181";
        /*
           param1:要连接的Zookeeper服务器列表
           param2:会话的超时时间
           param3:链接超时时间
           param4:重试策略
         */
        CuratorFramework client = CuratorFrameworkFactory.newClient(connectionStr,8000,8000,retryPolicy);
        //3.开启客户端
        client.start();
        //4.创建节点
        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/hello","world".getBytes());
        //5.关闭客户端
        client.close();
    }
}

创建临时节点

//4.创建节点
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/tmp","world".getBytes());
Thread.sleep(5000);

进程结束会话结束,临时节点消失。

修改节点数据

//4.修改节点数据
client.setData().forPath("/hello","aaa".getBytes());

获取节点数据

//4.获取节点数据
byte[] data = client.getData().forPath("/hello");
System.out.println(new String(data));

节点watch机制

@Test
public void watchZnode() throws Exception {
    //1.定制重试策略
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 1);
    //2.获取客户端
    String connectionStr = "192.168.217.100:2181,192.168.217.110:2181,192.168.217.120:2181";
    CuratorFramework client = CuratorFrameworkFactory.newClient(connectionStr,8000,8000,retryPolicy);
    //3.启动客户端
    client.start();
    //4.创建一个TreeCache对象,指定要监控的节点路径
    TreeCache treeCache = new TreeCache(client,"/hello");
    //5.自定义一个监听器
    treeCache.getListenable().addListener(new TreeCacheListener() {
        @Override
        public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
            ChildData data = treeCacheEvent.getData();
            if(data!=null){
                switch (treeCacheEvent.getType()){
                    case NODE_ADDED:
                        System.out.println("新增节点!");
                        break;
                    case NODE_REMOVED:
                        System.out.println("移除节点!");
                        break;
                    case NODE_UPDATED:
                        System.out.println("更新节点!");
                        break;
                    default:
                        break;
                }
            }
        }
    });
    //6.开始监听
    treeCache.start();
    Thread.sleep(100000);
    client.close();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值