Hadoop各组件详解(Zookeeper篇)

一、ZooKeeper概述

1.Zookeeper简介

  • (1)Zookeeper是一个分布式协调服务的开源框架。主要用来解决分布式集群中应用系统的一致性问题
  • (2)ZooKeeper本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理

2.Zookeeper特性

  • (1)全局数据一致:集群中每个服务器保存一份相同的数据副本,client无论连接到哪个服务器,展示的数据都是一致的,这是最重要的特征
  • (2)可靠性:如果消息被其中一台服务器接受,那么将被所有的服务器接受
  • (3)顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面
  • (4)数据更新原子性:一次数据更新要么成功(半数以上节点成功),要么失败,不存在中间状态
  • (5)实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息

3.Zookeeper集群角色

在这里插入图片描述
Leader:

1.Zookeeper集群工作的核心
2.事务请求(写操作)的唯一调度和处理者,保证集群事务处理的顺序性
3.集群内部各个服务器的调度者
4.对于create,setData,delete等有写操作的请求,则需要统一转发给leader处理,
leader需要决定编号、执行操作,这个过程称为一个事务

Follower:

1.处理客户端非事务(读操作)请求,转发事务请求给Leader
2.参与集群Leader选举投票
3.此外,针对访问量比较大的zookeeper集群,还可新增观察者角色

Observer:

1.观察者角色,观察Zookeeper集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,
对于事务请求,则会转发给Leader服务器进行处理
2.不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力

二、ZooKeeper安装

安装zookeeper的注意事项:
	安装前需要安装好jdk
	检测集群时间是否同步
	检测防火墙是否关闭
	检测主机 ip映射有没有配置
	
##下载安装包、解压
下载地址:
CDH版本:http://archive.cloudera.com/cdh5/cdh/5/zookeeper-3.4.5-cdh5.14.0.tar.gz
apache版本:https://archive.apache.org/dist/zookeeper/

1.解压tar包

tar -zxf zookeeper-3.4.5-cdh5.14.0.tar.gz ../servers/

2.配置环境变量

vim /etc/profile.d/zookeeper.sh

export ZOOKEEPER_HOME=/export/servers/zookeeper
export PATH=$PATH:$ZOOKEEPER_HOME/bin

source /etc/profile

3.修改配置文件

cd $ZOOKEEPER_HOME/conf
mv zoo_sample.cfg  zoo.cfg
vim zoo.cfg

在这里插入图片描述

4.添加myid

扫描二维码关注公众号,回复: 9209100 查看本文章
创建在 zoo.cfg 中指定的 dataDir 目录
mkdir /export/servers/zookeeper/zkdata

进入到目录
cd /export/servers/zookeeper/zkdata

给当前节点添加 myid
echo 1 > myid

5.分发zookeeper

scp -r zookeeper node02:`pwd`

分发完后根据需求修改 myid 文件中的值大小

6.启动zookeeper

每台节点使用命令 `zkServer.sh start` 进行启动
然后使用命令 `zkServer.sh status` 进行查看状态

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、ZooKeeper Shell

1、客户端连接

运行 `zkCli.sh –server ip`   进入命令行工具
输入help,输出zk shell提示:

在这里插入图片描述

2、shell基本操作

(1)创建节点
语法:create [-s] [-e] path data acl

其中,-s 指定为序列化节点 、 -e 指定为临时节点 ; 若不指定,则表示永久非序列化节点,acl用来进行权限控制

在这里插入图片描述

(2)读取节点
与读取相关的命令有 ls 命令 和 get 命令
1.ls 命令可以列出Zookeeper指定节点下的所有子节点,只能查看指定节点下的第一级的所有子节点
2.get 命令可以获取Zookeeper指定节点的数据内容和属性信息

语法:
ls path [watch]
get path [watch]
ls2 path [watch]

在这里插入图片描述

(3)更新节点
语法:set path data [version]
	data就是要更新的新内容,version表示数据版本

在这里插入图片描述
现在dataVersion已经变为2了,表示进行了2次更新

(4)删除节点
语法:delete path [version]
	若删除节点存在子节点,那么无法删除该节点,必须先删除子节点,再删除父节点
语法:Rmr path
	递归删除节点

在这里插入图片描述
在这里插入图片描述

(5)其他节点相关命令
语法:setquota -n|-b val path  
	对节点增加限制
		n:表示子节点的最大个数
		b:表示数据值的最大长度
		val:子节点最大个数或数据值的最大长度
		path:节点路径

在这里插入图片描述

语法:listquota path  
	列出指定节点的quota

在这里插入图片描述

语法:delquota [-n|-b] path  
	删除quota

在这里插入图片描述

语法:history  
	列出命令历史

在这里插入图片描述

语法:redo 编号
该命令可以重新执行指定命令编号的历史命令,命令编号可以通过history查看

在这里插入图片描述

四、ZooKeeper 数据模型

ZooKeeper的数据模型,在结构上和标准文件系统的非常相似,拥有一个层次的命名空间,都是采用树形层次结构,ZooKeeper树中的每个节点被称为—Znode。和文件系统的目录树一样,ZooKeeper树中的每个节点可以拥有子节点。但也有不同之处:

  • (1)Znode兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分,并可以具有子Znode。用户对Znode具有增、删、改、查等操作(权限允许的情况下)
  • (2)Znode具有原子性操作,读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的操作
  • (3)Znode存储数据大小有限制。ZooKeeper虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据,通常以KB为大小单位。ZooKeeper的服务器和客户端都被设计为严格检查并限制每个Znode的数据大小至多1M,当时常规使用中应该远小于此值
  • (4)Znode通过路径引用,如同Unix中的文件路径。路径必须是绝对的,因此他们必须由斜杠字符来开头。除此以外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。在ZooKeeper中,路径由Unicode字符串组成,并且有一些限制。字符串"/zookeeper"用以保存管理信息,比如关键配额信息

4.1 数据结构图

在这里插入图片描述

  • 图中的每个节点称为一个Znode。 每个Znode由3部分组成:
    • ① stat:此为状态信息, 描述该Znode的版本, 权限等信息
    • ② data:与该Znode关联的数据
    • ③ children:该Znode下的子节点

4.2 节点类型

  • Znode有两种,分别为临时节点和永久节点
  • 节点的类型在创建时即被确定,并且不能改变
  • 临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话结束,临时节点将被自动删除,当然可以也可以手动删除;临时节点不允许拥有子节点
  • 永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除
  • Znode还有一个序列化的特性,如果创建的时候指定的话,该Znode的名字后面会自动追加一个不断增加的序列号。序列号对于此节点的父节点来说是唯一的,这样便会记录每个子节点创建的先后顺序。它的格式为“%10d”(10位数字,没有数值的数位用0补充,例如“0000000001”)
    在这里插入图片描述
  • 这样便会存在四种类型的Znode节点,分别对应:
    • PERSISTENT:永久节点
    • EPHEMERAL:临时节点
    • PERSISTENT_SEQUENTIAL:永久节点、序列化
    • EPHEMERAL_SEQUENTIAL:临时节点、序列化

4.3 节点属性

每个znode都包含了一系列的属性,通过命令get,可以获得节点的属性
在这里插入图片描述

  • dataVersion:数据版本号,每次对节点进行set操作,dataVersion的值都会增加1(即使设置的是相同的数据),可有效避免了数据更新时出现的先后顺序问题
  • cversion :子节点的版本号。当znode的子节点有变化时,cversion 的值就会增加1
  • cZxid :Znode创建的事务id
  • mZxid :Znode被修改的事务id,即每次对znode的修改都会更新mZxid
  • 对于zk来说,每次的变化都会产生一个唯一的事务id,zxid(ZooKeeper Transaction Id)。通过zxid,可以确定更新操作的先后顺序。例如,如果zxid1小于zxid2,说明zxid1操作先于zxid2发生,zxid对于整个zk都是唯一的,即使操作的是不同的znode
  • ctime:节点创建时的时间戳
  • mtime:节点最新一次更新发生时的时间戳
  • ephemeralOwner:如果该节点为临时节点, ephemeralOwner值表示与该节点绑定的session id. 如果不是, ephemeralOwner值为0
  • 在client和server通信之前,首先需要建立连接,该连接称为session。连接建立后,如果发生连接超时、授权失败,或者显式关闭连接,连接便处于CLOSED状态, 此时session结束

五、ZooKeeper Watcher(监听机制)

简介

  • ZooKeeper提供了分布式数据发布/订阅功能,一个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能让多个订阅者同时监听某一个主题对象,当这个主题对象自身状态变化时,会通知所有订阅者,使他们能够做出相应的处理
  • ZooKeeper中,引入了Watcher机制来实现这种分布式的通知功能。ZooKeeper允许客户端向服务端注册一个Watcher监听,当服务端的一些事件触发了这个Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。
  • 触发事件种类很多,如:节点创建,节点删除,节点改变,子节点改变等
    总的来说可以概括Watcher为以下三个过程:客户端向服务端注册Watcher、服务端事件发生触发Watcher、客户端回调Watcher得到触发事件情况

5.1 Watch机制特点

一次性触发

事件发生触发监听,一个watcher event就会被发送到设置监听的客户端,这种效果是一次性的,后续再次发生同样的事件,不会再次触发

事件封装

ZooKeeper使用WatchedEvent对象来封装服务端事件并传递
WatchedEvent包含了每一个事件的三个基本属性:
通知状态(keeperState),事件类型(EventType)和节点路径(path)

event异步发送

watcher的通知事件从服务端发送到客户端是异步的

先注册再触发

Zookeeper中的watch机制,必须客户端先去服务端注册监听,这样事件发送才会触发监听,通知给客户端

5.2 通知状态和事件类型

同一个事件类型在不同的通知状态中代表的含义有所不同,下表列举了常见的通知状态和事件类型
在这里插入图片描述
其中连接状态事件(type=None, path=null)不需要客户端注册,客户端只要有需要直接处理就行了

5.3 Shell 客户端设置watcher

设置节点数据变动监听:

在这里插入图片描述

通过另一个客户端更改节点数据:

在这里插入图片描述

此时设置监听的节点收到通知:

在这里插入图片描述

六、ZooKeeper Java API

简介

org.apache.zookeeper.Zookeeper
Zookeeper 是在Java中客户端主类,负责建立与zookeeper集群的会话,并提供方法进行操作
org.apache.zookeeper.Watcher
Watcher接口表示一个标准的事件处理器,其定义了事件通知相关的逻辑,包含KeeperState和EventType两个枚举类,
分别代表了通知状态和事件类型,同时定义了事件的回调方法:process(WatchedEvent event)
process方法是Watcher接口中的一个回调方法,当ZooKeeper向客户端发送一个Watcher事件通知时,
客户端就会对相应的process方法进行回调,从而实现对事件的处理

基本使用

建立Java maven项目,引入maven pom坐标

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.9</version>
</dependency>

示例1

public static void main(String[] args) throws Exception {
        // 初始化 ZooKeeper实例(zk地址、会话超时时间,与系统默认一致、watcher)
        ZooKeeper zk = new ZooKeeper("node01:2181,node02:2181", 30000, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("事件类型为:" + event.getType());
                System.out.println("事件发生的路径:" + event.getPath());
                System.out.println("通知状态为:" +event.getState());
            }
        });
	zk.create("/tmp", "HelloWorld".getBytes("UTF-8"), Ids.OPEN_ACL_UNSAFE,
		 CreateMode.PERSISTENT);
    zk.close();
}

示例2

public static void main(String[] args) throws Exception {
        // 初始化 ZooKeeper实例(zk地址、会话超时时间,与系统默认一致、watcher)
        ZooKeeper zk = new ZooKeeper("node01:2181,node02:2181", 30000, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("事件类型为:" + event.getType());
                System.out.println("事件发生的路径:" + event.getPath());
                System.out.println("通知状态为:" +event.getState());
            }
        });
 // 创建一个目录节点
 zk.create("/testRootPath", "testRootData".getBytes(), Ids.OPEN_ACL_UNSAFE,
   CreateMode.PERSISTENT); 
 // 创建一个子目录节点
 zk.create("/testRootPath/testChildPathOne", "testChildDataOne".getBytes(),
   Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); 
 System.out.println(new String(zk.getData("/testRootPath",false,null))); 
 // 取出子目录节点列表
 System.out.println(zk.getChildren("/testRootPath",true)); 
 // 修改子目录节点数据
 zk.setData("/testRootPath/testChildPathOne","modifyChildDataOne".getBytes(),-1); 
 System.out.println("目录节点状态:["+zk.exists("/testRootPath",true)+"]"); 
 // 创建另外一个子目录节点
 zk.create("/testRootPath/testChildPathTwo", "testChildDataTwo".getBytes(), 
   Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); 
 System.out.println(new String(zk.getData("/testRootPath/testChildPathTwo",true,null))); 
 // 删除子目录节点
 zk.delete("/testRootPath/testChildPathTwo",-1); 
 zk.delete("/testRootPath/testChildPathOne",-1); 
 // 删除父目录节点
 zk.delete("/testRootPath",-1);
 zk.close();
}

六、ZooKeeper选举机制

默认算法:
Zookeeper默认的算法是FastLeaderElection,采用投票数大于半数则胜出的逻辑

概念

  • 服务器ID
    比如有三台服务器,编号分别是1,2,3
    编号越大在选择算法中的权重越大
  • 选举状态
    LOOKING,竞选状态
    FOLLOWING,随从状态,同步leader状态,参与投票
    OBSERVING,观察状态,同步leader状态,不参与投票
    LEADING,领导者状态
  • 数据ID
    服务器中存放的最新数据version
    值越大说明数据越新,在选举算法中数据越新权重越大
  • 逻辑时钟
    也叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断

全新集群选举

假设目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:

  • 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking
  • 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING
  • 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟
  • 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟
  • 服务器5启动,后面的逻辑同服务器4成为小弟

非全新集群选举

对于运行正常的zookeeper集群,中途有机器down掉,需要重新选举时,选举过程就需要加入数据ID、服务器ID和逻辑时钟

  • 数据ID:数据新的version就大,数据每次更新都会更新version
  • 服务器ID:就是我们配置的myid中的值,每个机器一个
  • 逻辑时钟:这个值从0开始递增,每次选举对应一个值。 如果在同一次选举中,这个值是一致的
  • 这样选举的标准就变成:
    1、逻辑时钟小的选举结果被忽略,重新投票
    2、统一逻辑时钟后,数据id大的胜出
    3、数据id相同的情况下,服务器id大的胜出
    根据这个规则选出leader
发布了52 篇原创文章 · 获赞 282 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_43733123/article/details/103476235