08_ZooKeeper一

08_ZooKeeper一_第1张图片

 

 

 

目标

  1. 学会ZooKeeper的基本使用:命令行、Java编程

  2. 理解ZooKeeper watcher监听器工作原理:注册、监听事件、回调函数(考点)

  3. 能独立描述出ZooKeeper选举过程(难点、考点)

  4. 理解、并讲述客户端从ZooKeeper读写的过程(考点)

一、ZooKeeper概述

1. 为什么要用ZooKeeper

  • 分布式框架多个独立的程序协同工作比较复杂。开发人员容易花较多的精力实现如何使多个程序协同工作的逻辑,导致没有时间更好的思考实现程序本身的逻辑,或者开发人员对程序间的协同工作关注不够,造成协调问题,且这个分布式框架中协同工作的逻辑是共性的需求

  • ZooKeeper简单易用,能够很好的解决分布式框架在运行中,出现的各种协调问题。

  • 比如集群master主备切换、节点的上下线感知、统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等等

2、什么是ZooKeeper?

  • ZooKeeper(主从架构)

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

    • 是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。

    • 是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    • 目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户

  • Zookeeper 作为一个分布式的服务框架

    • 主要用来解决分布式集群中应用系统的一致性问题

    • 它能提供基于类似于文件系统的目录节点树方式的数据存储,

    • Zookeeper 作用主要是用来 维护和监控存储的数据的状态变化,通过监控这些数据状态的变化,从而达到基于数据的集群管理

 

3、ZooKeeper应用场景

  • ZooKeeper应用场景

08_ZooKeeper一_第2张图片

 

  1. NameNode使用ZooKeeper实现高可用.

  2. Yarn ResourceManager使用ZooKeeper实现高可用.

  3. 利用ZooKeeper对HBase集群做高可用配置

  4. kafka使用ZooKeeper

    • 保存消息消费信息比如offset.

    • 用于检测崩溃

    • 主题topic发现

    • 保持主题的生产和消费状态

 

二. ZooKeeper应用使用

从下图观察:ZooKeeper集群目前有两种角色:leader、follower;

ZooKeeper集群也是主从架构的:leader为主follower为从

08_ZooKeeper一_第3张图片

通过客户端操作ZooKeeper集群,有两种类型的客户端

①命令行zkCli

②Java编程

 

2.1 zkCli命令行(30分钟)

1、集群命令(每个节点运行此命令)

客户端连接zkServer服务器

08_ZooKeeper一_第4张图片

# 1、启动ZooKeeper集群;在ZooKeeper集群中的每个节点执行此命令
${ZK_HOME}/bin/zkServer.sh start


# 2、停止ZooKeeper集群(每个节点执行以下命令)
${ZK_HOME}/bin/zkServer.sh stop


# 3、查看集群状态(每个节点执行此命令)
${ZK_HOME}/bin/zkServer.sh status

# 4、使用ZooKeeper自带的脚本,连接ZooKeeper的服务器
bin/zkCli.sh -server node01:2181,node02:2181,node03:2181

说明:-server选项后指定参数node01:2181,node02:2181,node03:2181,客户端随机的连接三个服务器中的一个 ,客户端发出对ZooKeeper集群的读写请求

ZooKeeper集群中有类似于linux文件系统的一个简版的文件系统;目录结构也是树状结构(目录树

 

2、常用命令

# 1、查看ZooKeeper根目录/下的文件列表
ls /

# 2、创建节点,并指定数据
create /kkb    kkb

# 3、获得某节点的数据
get /kkb

# 4、修改节点的数据
set /kkb kkb01

# 5、删除节点
delete /kkb

3.2 Java API编程(重点 30分钟)

IDE可以是eclipse,或IDEA;此处以IDEA演示

编程分两类:原生API编程;curator编程

  • Curator官网

  • Curator编程

    • Curator对ZooKeeper的api做了封装,提供简单易用的api;

    • 它的风格是Curator链式编程

    • 参考《使用curator做zk编程》

  • 代码详见工程代码

  • package com.kaikeba.zookeeper.curator;
    
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.framework.recipes.cache.ChildData;
    import org.apache.curator.framework.recipes.cache.TreeCache;
    import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
    import org.apache.curator.framework.recipes.cache.TreeCacheListener;
    import org.apache.curator.retry.RetryNTimes;
    import org.apache.zookeeper.CreateMode;
    
    public class CuratorClientTest {
        //根据自己集群的实际情况,Zookeeper info 替换
        private static final String ZK_ADDRESS = "node01:2181,node02:2181,node03:2181";
        private static final String ZK_PATH = "/zk_test";
    
        static CuratorFramework client = null;
    
        // 方法:初始化,建立连接
        public static void init() {
    
            // 1、创建客户端
                // 1.1、重试连接策略,失败重试次数;每次休眠5000毫秒
               //RetryPolicy policy = new ExponentialBackoffRetry(3000, 3);
            RetryNTimes retryPolicy = new RetryNTimes(10, 5000);
    
               // 1.2、设置客户端参数,参数1:指定连接的服务器集端口列表;参数2:重试策略
            client = CuratorFrameworkFactory.newClient(ZK_ADDRESS, retryPolicy);
    
              // 1.3、启动客户端,连接到zk集群
            client.start();
            System.out.println("zk client start successfully!");
        }
    
        // 方法:关闭连接
        public static void clean() {
            System.out.println("close session");
            client.close();
        }
    
        // 方法:创建永久节点
        public static void createPersistentZNode() throws Exception {
            String zNodeData = "火辣的";
            ///a/b/c
            client.create().
                    creatingParentsIfNeeded().
                    withMode(CreateMode.PERSISTENT).
                    forPath("/beijing/goddess/tingting", zNodeData.getBytes());
    
        }
    
        //  方法:创建临时节点
        public static void createEphemeralZNode() throws Exception {
            String zNodeData2 = "hello3";
            client.create().
                    creatingParentsIfNeeded().
                    withMode(CreateMode.EPHEMERAL).
                    forPath("/hello3/zk", zNodeData2.getBytes());
    
            Thread.sleep(10000);
        }
    
        public static void queryZNodeData() throws Exception {
            // 查询列表
            print("ls", "/");
            print(client.getChildren().forPath("/"));
    
            //查询节点数据
            print("get", ZK_PATH);
            print(client.getData().forPath(ZK_PATH));
        }
    
        // 修改节点数据
        public static void modifyZNodeData() throws Exception {
    
            String data2 = "world";
            print("set", ZK_PATH, data2);
    
            client.setData().forPath(ZK_PATH, data2.getBytes());
            print("get", ZK_PATH);
            print(client.getData().forPath(ZK_PATH));
        }
    
        public static void deleteZNode() throws Exception {
            // 删除节点
            print("delete", ZK_PATH);
            client.delete().forPath(ZK_PATH);
    
            print("ls", "/");
            print(client.getChildren().forPath("/"));
        }
    
        //监听ZNode
        public static void watchZNode() throws Exception {
    
            //设置节点的cache  cache分为三种TreeCache、
            TreeCache treeCache = new TreeCache(client, "/zk_test");
    
            //设置监听器和处理过程 匿名xx类
            treeCache.getListenable().addListener(new TreeCacheListener() {
                @Override
                public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
                    ChildData data = event.getData();
                    if(data !=null){
                        switch (event.getType()) {
                            case NODE_ADDED:
                                System.out.println("NODE_ADDED : "+ data.getPath() +"  数据:"+ new String(data.getData()));
                                break;
                            case NODE_REMOVED:
                                System.out.println("NODE_REMOVED : "+ data.getPath() +"  数据:"+ new String(data.getData()));
                                break;
                            case NODE_UPDATED:
                                System.out.println("NODE_UPDATED : "+ data.getPath() +"  数据:"+ new String(data.getData()));
                                break;
    
                            default:
                                break;
                        }
                    }else{
                        System.out.println( "data is null : "+ event.getType());
                    }
                }
            });
            //开始监听
            treeCache.start();
            Thread.sleep(60000);
            //关闭cache
            System.out.println("关闭cache");
            treeCache.close();
        }
    
        public static void main(String[] args) throws Exception {
    
            init();
           // createPersistentZNode();
    //      createEphemeralZNode();
       //  queryZNodeData();
    //        modifyZNodeData();
    //        deleteZNode();
           watchZNode();
            clean();
    
        }
    
        private static void print(String... cmds) {
            StringBuilder text = new StringBuilder("$ ");
            for (String cmd : cmds) {
                text.append(cmd).append(" ");
            }
            System.out.println(text.toString());
        }
    
        private static void print(Object result) {
            System.out.println(
                    result instanceof byte[]
                            ? new String((byte[]) result)
                            : result);
        }
    }
    

     

 

三、基本概念和操作(25分钟)

分布式通信有几种方式

1、直接通过网络连接的方式进行通信;

2、通过共享存储的方式,来进行通信或数据的传输

ZooKeeper使用第二种方式,提供分布式协调服务

3.1 ZooKeeper数据结构

ZooKeeper主要由以下三个部分实现

ZooKeeper  =   ①简版文件系统(Znode) :基于类似于文件系统的目录节点树方式的数据存储

                         +②原语                            :可简单理解成ZooKeeper的基本的命令

                         +③通知机制(Watcher)。

 

3.2 数据节点ZNode

3.2.1 什么是ZNode

  • ZNode 分为四类:

  持久节点 临时节点
非有序节点 create create -e
有序节点 create -s create -s -e

 

3.2.2 持久节点

  • 类比,文件夹

# 创建节点/zk_test,并设置数据my_data
create /zk_test my_data

# 持久节点,只有显示的调用命令,才能删除永久节点
delete /zk_test

3.2.3 临时节点

  • 临时节点的生命周期跟客户端会话session绑定,一旦会话失效,临时节点被删除。

# client1上创建临时节点
create -e /tmp tmpdata
​
# client2上查看client1创建的临时节点
ls /
​
# client1断开连接
close
​
# client2上观察现象,发现临时节点被自动删除
ls /

3.2.4 有序节点

  • ZNode也可以设置为有序节点

  • 为什么设计有序节点

    • 防止多个不同的客户端在同一目录下,创建同名ZNode,由于重名,导致创建失败

  • 如何创建临时节点

    • 命令行使用-s选项:create -s /kkb kkb

    • Curator编程,可添加一个特殊的属性:CreateMode.EPHEMERAL

       

  • 一旦节点被标记上这个属性,那么在这个节点被创建时,ZooKeeper 就会自动在其节点后面追加上一个整型数字

    # 创建持久、有序节点
    create -s /test01 test01-data
    # Created /test010000000009
    • 这个整数是一个由父节点维护的自增数字。

    • 提供了创建唯一名字的ZNode的方式

3.3 会话(Session)

 

3.4.1 什么是会话

  • 客户端要对ZooKeeper集群进行读写操作,得先与某一ZooKeeper服务器建立TCP长连接;此TCP长连接称为建立一个会话Session

  • 每个会话有超时时间:SessionTimeout

    • 当客户端与集群建立会话后,如果超过SessionTimeout时间,两者间没有通信,会话超时

3.4.2 会话的特点

  • 客户端打开一个Session中的请求以FIFO(先进先出)的顺序执行;

    • 如客户端client01与集群建立会话后,先发出一个create请求,再发出一个get请求;

    • 那么在执行时,会先执行create,再执行get

  • 若打开两个Session,无法保证Session间,请求FIFO执行;只能保证一个session中请求的FIFO

3.4.3 会话的生命周期

 

  • 会话的生命周期:

    • 未建立连接

    • 正在连接

    • 已连接

    • 关闭连接

3.4 请求

  • 读写请求

    • 通过客户端向ZooKeeper集群中写数据

    • 通过客户端从ZooKeeper集群中读数据

 

3.5 事务zxid

  • 事务

    • 客户端的写请求,会对ZooKeeper中的数据做出更改;如增删改的操作

    • 每次写请求,会生成一次事务

    • 每个事务有一个全局唯一的事务ID,用 ZXID 表示;全局自增

  • 事务特点

    • ACID:

    • 原子性atomicity | 一致性consistency | 隔离性isolation | 持久性durability

  • ZXID结构:

    • 通常是一个64位的数字。由32位epoch+32位counter组成

    • epoch、counter各32位

 

3.6 Watcher监视与通知

4.6.1 为什么要有Watcher

  • 问:客户端如何获取ZooKeeper服务器上的最新数据

    • 方式一 : 轮询,ZooKeeper以远程服务的方式,被客户端访问;客户端以轮询的方式获得znode数据,效率会比较低(代价比较大)

     

    • 方式二: 基于通知的机制:

      • 客户端在znode上注册一个Watcher监视器

      • 当znode上数据出现变化,watcher监测到此变化,通知客户端

     

  • 对比,那种好?

4.6.2 什么是Watcher?

  • 客户端在服务器端,注册的事件监听器

  • watcher用于监听znode上的某些事件

    • 比如znode数据修改、节点增删等;

    • 当监听到事件后,watcher会触发通知客户端

4.6.3 如何设置Watcher

注意:Watcher是一个单次触发的操作

  • 可以设置watcher的命令如下:

 

  • 示例1

#ls path [watch]
#node01 上执行
ls /zk_test watch
​
#node02 上执行
create /zk_test/dir01 dir01-data
​
#观察node-01上变化
[zk: node-01:2181,node-02:2181,node-03:2181(CONNECTED) 87] 
WATCHER::
​
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/zk_test
#node01 上执行
ls /zk_test watch
​
#node02 上执行
create /zk_test/dir01 dir01-data
​
#观察node-01上变化
[zk: node-01:2181,node-02:2181,node-03:2181(CONNECTED) 87] 
WATCHER::
​
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/zk_test

图示:

  • client1上执行步骤1

  • client2上执行步骤2;

  • client1上观察现象3

 

 

  • 示例2

#监控节点数据的变化;
#node02上
get /zk_test watch
​
#node03上
set /zk_test "junk01"
#观察node2上cli的输出,检测到变化
#node02上
get /zk_test watch
​
#node03上
set /zk_test "junk01"
#观察node2上cli的输出,检测到变化
  • 示例3:节点上下线监控

    • 原理:

      1. 节点1(client1)创建临时节点

      2. 节点2(client2)在临时节点,注册监听器watcher

      3. 当client1与zk集群断开连接,临时节点会被删除

      4. watcher发送消息,通知client2,临时节点被删除的事件

    • 用到的zk特性:

      Watcher+临时节点

    • 好处:

      通过这种方式,检测和被检测系统不需要直接关联(如client1与client2),而是通过ZK上的某个节点进行关联,大大减少了系统耦合

    • 实现:

      client1操作

      # 创建临时节点
      create -e /zk_tmp tmp-data
      create -e /zk_tmp tmp-data

      client2操作

      # 在/zk_tmp注册监听器
      ls /zk_tmp watch
      ls /zk_tmp watch

      client1操作

      # 模拟节点下线
      close
      close

      观察client2

      WATCHER::
      ​
      WatchedEvent state:SyncConnected type:NodeDeleted path:/zk_tmp
      ​
      WatchedEvent state:SyncConnected type:NodeDeleted path:/zk_tmp
    • 图示:

      client1:

       

      client2:

 

4.7 总结

 

四、ZooKeeper工作原理(5分钟)

  • ZooKeeper使用原子广播协议叫做Zab(ZooKeeper Automic Broadcast)协议

  • Zab协议有两种模式

    • 恢复模式(选主):因为ZooKeeper也是主从架构;当ZooKeeper集群没有主的角色leader时,从众多服务器中选举leader时,处于此模式

    • 广播模式(同步):当集群有了leader后,客户端向ZooKeeper集群读写数据时,集群处于此模式

  • 为了保证事务的顺序一致性,ZooKeeper采用了递增的事务id号(zxid)来标识事务,所有提议(proposal)都有zxid

 

五、访问控制ACL

5.8 ACL访问控制列表

5.1、ACL概述

1、 zk做为分布式架构中的重要中间件,通常会在上面以节点的方式存储一些关键信息,

        默认情况下,所有应用都可以读任何节点,在复杂的应用中,这不太安全,

         ZK通过ACL机制来解决访问权限问题

  2、ACL(Access Control List)可以设置某些客户端,对zookeeper服务器上节点的权限,如增删改

  3、ZooKeeper 采用 ACL(Access Control Lists)策略来进行权限控制。ZooKeeper 定义了如下5种权限。

  • (1)CREATE: 创建子节点的权限。

  • (2)READ: 获取节点数据和子节点列表的权限。

  • (3)WRITE:更新节点数据的权限。

  • (4)DELETE: 删除子节点的权限。

  • (5)ADMIN: 设置节点ACL的权限。

注意:CREATE 和 DELETE 都是针对子节点的权限控制。

5.3 如何设置ACL

 

  • 1、五种权限简称

    • CREATE -> 增 -> c

    • READ -> 查 -> r

    • WRITE -> 改 -> w

    • DELETE -> 删 -> d

    • ADMIN -> 管理 -> a

    • 这5种权限简写为crwda

  • 2、鉴权模式

    • world:默认方式,相当于全世界都能访问

    • auth:代表已经认证通过的用户(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授权用户)

    • digest:即用户名:密码这种方式认证,这也是业务系统中最常用的

    • ip:使用Ip地址认证

 

  • 3、演示auth方式
# 1)增加一个认证用户
# addauth digest 用户名:密码明文
addauth digest kkb:kkb
​
# 2)设置权限
# setAcl /path auth:用户名:密码明文:权限
setAcl /zk_test auth:kkb:kkb:rw
​
# 3)查看ACL设置
getAcl /zk_test

 

 

 

 

你可能感兴趣的:(hadoop)