大数据-Zookeeper
ZooKeeper基本概念
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
- Zookeeper是Google的Chubby的一个开源实现版。
- ZooKeeper是一个主从架构的分布式开源框架,对其他的分布式框架的提供协调服务(service)。
- Zookeeper作为一个分布式的服务框架提供类似于linux文件系统(有目录节点树)的简版文件系统来存储数据。
- Zookeeper维护和监控存储的数据的状态变化,通过监控这些数据状态的变化,从而达到基于数据的集群管理,主要用来解决分布式集群中应用系统的一致性问题。
ZooKeeper使用场景
-
分布式框架中多个独立的程序协同工作比较复杂
在分布式框架中协同工作的逻辑一般是相同的,如果开发人员花较多的精力实现如何使多个程序协同工作,会导致没有时间更好的思考实现程序本身的业务逻辑;或者开发人员只考虑业务逻辑会造成对程序间的协同工作关注不够。
-
ZooKeeper简单易用,能够很好的解决分布式框架在运行中,出现的各种协调问题。
比如集群master主备切换、节点的上下线感知、统一命名服务、状态同步服务、集群管理、分布式应用配置管理等等。
ZooKeeper之初体验
ZooKeeper集群也是主从架构的:leader为主;follower为从
通过客户端操作ZooKeeper集群,有两种类型的客户端
- 命令行zkCli
- Java编程
zkCli命令行
集群命令
# 启动ZooKeeper集群;在ZooKeeper集群中的每个节点执行此命令
${ZK_HOME}/bin/zkServer.sh start
# 停止ZooKeeper集群(每个节点执行以下命令)
${ZK_HOME}/bin/zkServer.sh stop
# 查看集群状态(每个节点执行此命令)
${ZK_HOME}/bin/zkServer.sh status
客户端连接zkServer服务器
# 使用ZooKeeper自带的脚本,连接ZooKeeper的服务器
# -server选项后指定参数node01:2181,node02:2181,node03:2181
# 客户端随机的连接三个服务器中的一个
zkCli.sh -server node01:2181,node02:2181,node03:2181
常用命令
#创建节点,并指定数据
create /kkb kkb
#获得某节点的数据
get /kkb
#修改节点的数据
set /kkb kkb01
#删除节点
delete /kkb
基本概念和操作
分布式通信有几种方式:
- 直接通过网络连接的方式进行通信
- 通过共享存储的方式,来进行通信或数据的传输
ZooKeeper使用第二种方式,提供分布式协调服务。
ZooKeeper数据结构
ZooKeeper主要由以下三个部分实现:
ZooKeeper=①简版文件系统(Znode)+②原语+③通知机制(Watcher)。
-
ZK文件系统
-
基于类似于文件系统的目录节点树方式的数据存储
-
-
原语
-
可简单理解成ZooKeeper的基本的命令
-
-
Watcher(监听器)
数据节点ZNode
ZNode 分为四类
持久节点 | 临时节点 | |
非有序节点 | create | create -e |
有序节点 | create -s | create -s -e |
持久节点
# 类比,文件夹
# 创建节点/zk_test,并设置数据my_data
create /zk_test my_data
# 持久节点,只有显示的调用命令,才能删除永久节点
delete /zk_test
临时节点
#临时节点的生命周期跟客户端会话session绑定,一旦会话失效,临时节点被删除。
# client1上创建临时节点
create -e /tmp tmpdata
# client2上查看client1创建的临时节点
ls /
# client1断开连接
close
# client2上观察现象,发现临时节点被自动删除
ls /
有序节点
-
ZNode也可以设置为有序节点
-
为什么设计临时节点?
-
防止多个不同的客户端在同一目录下,创建同名ZNode,由于重名,导致创建失败
-
-
如何创建临时节点
-
命令行使用-s选项:create -s /kkb kkb
-
Curator编程,可添加一个特殊的属性:CreateMode.EPHEMERAL
-
- 一旦节点被标记上这个属性,那么在这个节点被创建时,ZooKeeper 就会自动在其节点后面追加上一个整型数字
-
这个整数是一个由父节点维护的自增数字。
-
提供了创建唯一名字的ZNode的方式
-
会话(Session)
什么是会话
-
客户端要对ZooKeeper集群进行读写操作,得先与某一ZooKeeper服务器建立TCP长连接;此TCP长连接称为建立一个会话Session。
-
每个会话有超时时间:SessionTimeout
-
当客户端与集群建立会话后,如果超过SessionTimeout时间,两者间没有通信,会话超时。
-
会话的特点
-
客户端打开一个Session中的请求以FIFO(先进先出)的顺序执行;
-
如客户端client01与集群建立会话后,先发出一个create请求,再发出一个get请求;
-
那么在执行时,会先执行create,再执行get
-
-
若打开两个Session,无法保证Session间,请求FIFO执行;只能保证一个session中请求的FIFO。
会话的生命周期
-
未建立连接
-
正在连接
-
已连接
-
关闭连接
请求
-
事务
-
客户端的写请求,会对ZooKeeper中的数据做出更改;如增删改的操作
-
每次写请求,会生成一次事务
-
每个事务有一个全局唯一的事务ID,用 ZXID 表示;全局自增
-
-
事务特点
-
ACID:
-
原子性atomicity | 一致性consistency | 隔离性isolation | 持久性durability
-
-
ZXID结构:
-
通常是一个64位的数字。由epoch+counter组成
-
epoch、counter各32位
-
Watcher监视与通知
问:客户端如何获取ZooKeeper服务器上的最新数据?
方式一轮询:ZooKeeper以远程服务的方式,被客户端访问;客户端以轮询的方式获得znode数据,效率会比较低(代价比较大)
方式二基于通知的机制:
-
客户端在znode上注册一个Watcher监视器
-
当znode上数据出现变化,watcher监测到此变化,通知客户端。
4.6.2 什么是Watcher?
-
客户端在服务器端,注册的事件监听器;
-
watcher用于监听znode上的某些事件
-
比如znode数据修改、节点增删等;
-
当监听到事件后,watcher会触发通知客户端
-
4.6.3 如何设置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
示例2:节点上下线监控
# client1操作
# 创建临时节点
create -e /zk_tmp tmp-data
# client2操作
# 在/zk_tmp注册监听器
ls /zk_tmp watch
# client1操作
# 模拟节点下线
close
# 观察client2
WATCHER::
WatchedEvent state:SyncConnected type:NodeDeleted path:/zk_tmp
ZooKeeper工作原理
-
ZooKeeper使用原子广播协议叫做Zab(ZooKeeper Automic Broadcast)协议
-
Zab协议有两种模式
-
恢复模式(选主):因为ZooKeeper也是主从架构;当ZooKeeper集群没有主的角色leader时,从众多服务器中选举leader时,处于此模式。
-
广播模式(同步):当集群有了leader后,客户端向ZooKeeper集群读写数据时,集群处于此模式。
-
-
为了保证事务的顺序一致性,ZooKeeper采用了递增的事务id号(zxid)来标识事务,所有提议(proposal)都有zxid。
HDFS HA方案
ZooKeeper监听器
关于ZooKeeper监听器有三个重要的逻辑:
-
注册:客户端向ZooKeeper集群注册监听器
-
监听事件:监听器负责监听特定的事件
-
回调函数:当监听器监听到事件的发生后,调用注册监听器时定义的回调函数
HDFS HA原理
-
在Hadoop 1.x版本,HDFS集群的NameNode一直存在单点故障问题:
-
集群只存在一个NameNode节点,它维护了HDFS所有的元数据信息
-
当该节点所在服务器宕机或者服务不可用,整个HDFS集群处于不可用状态
-
-
Hadoop 2.x版本提出了高可用 (High Availability, HA) 解决方案
①元数据同步
-
在同一个HDFS集群,运行两个互为主备的NameNode节点。
-
一台为主Namenode节点,处于Active状态,一台为备NameNode节点,处于Standby状态。
-
其中只有Active NameNode对外提供读写服务,Standby NameNode会根据Active NameNode的状态变化,在必要时切换成Active状态。
-
JournalNode集群
-
在主备切换过程中,新的Active NameNode必须确保与原Active NamNode元数据同步完成,才能对外提供服务
-
所以用JournalNode集群作为共享存储系统;
-
当客户端对HDFS做操作,会在Active NameNode中edits.log文件中作日志记录,同时日志记录也会写入JournalNode集群;负责存储HDFS新产生的元数据
-
当有新数据写入JournalNode集群时,Standby NameNode能监听到此情况,将新数据同步过来
-
Active NameNode(写入)和Standby NameNode(读取)实现元数据同步
-
另外,所有datanode会向两个主备namenode做block report
-
②主备切换
-
ZKFC涉及角色
-
每个NameNode节点上各有一个ZKFC进程
-
ZKFC即ZKFailoverController,作为独立进程存在,负责控制NameNode的主备切换
-
ZKFC会监控NameNode的健康状况,当发现Active NameNode异常时,通过Zookeeper集群进行namenode主备选举,完成Active和Standby状态的切换
-
ZKFC在启动时,同时会初始化HealthMonitor和ActiveStandbyElector服务
-
ZKFC同时会向HealthMonitor和ActiveStandbyElector注册相应的回调方法(如上图的①回调、②回调)
-
HealthMonitor定时调用NameNode的HAServiceProtocol RPC接口(monitorHealth和getServiceStatus),监控NameNode的健康状态,并向ZKFC反馈
-
ActiveStandbyElector接收ZKFC的选举请求,通过Zookeeper自动完成namenode主备选举
-
选举完成后回调ZKFC的主备切换方法对NameNode进行Active和Standby状态的切换
-
-
主备选举过程:
-
启动两个NameNode、ZKFC
-
两个ZKFC通过各自ActiveStandbyElector发起NameNode的主备选举,这个过程利用Zookeeper的写一致性和临时节点机制实现
-
当发起一次主备选举时,ActiveStandbyElector会尝试在Zookeeper创建临时节点
/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock
,Zookeeper的写一致性保证最终只会有一个ActiveStandbyElector创建成功 -
ActiveStandbyElector从ZooKeeper获得选举结果
-
创建成功的 ActiveStandbyElector回调ZKFC的回调方法②,将对应的NameNode切换为Active NameNode状态
-
而创建失败的ActiveStandbyElector回调ZKFC的回调方法②,将对应的NameNode切换为Standby NameNode状态
-
不管是否选举成功,所有ActiveStandbyElector都会在临时节点ActiveStandbyElectorLock上注册一个Watcher监听器,来监听这个节点的状态变化事件
-
如果Active NameNode对应的HealthMonitor检测到NameNode状态异常时,通知对应ZKFC
-
ZKFC会调用 ActiveStandbyElector 方法,删除在Zookeeper上创建的临时节点ActiveStandbyElectorLock(或者ActvieStandbyElector与ZooKeeper的session断开,临时节点也会被删除,但有可能此时原Active NameNode仍然是active状态)
-
此时,Standby NameNode的ActiveStandbyElector注册的Watcher就会监听到此节点的 NodeDeleted事件。
-
收到这个事件后,此ActiveStandbyElector发起主备选举,成功创建临时节点ActiveStandbyElectorLock,如果创建成功,则Standby NameNode被选举为Active NameNode(过程同上)
如何防止脑裂
-
脑裂
在分布式系统中双主现象又称为脑裂,由于Zookeeper的“假死”、长时间的垃圾回收或其它原因都可能导致双Active NameNode现象,此时两个NameNode都可以对外提供服务,无法保证数据一致性
-
隔离
对于生产环境,这种情况的出现是毁灭性的,必须通过自带的隔离(Fencing)机制预防此类情况
-
原理
-
ActiveStandbyElector成功创建ActiveStandbyElectorLock临时节点后,会创建另一个ActiveBreadCrumb持久节点
-
ActiveBreadCrumb持久节点保存了Active NameNode的地址信息
-
当Active NameNode在正常的状态下断开Zookeeper Session,会一并删除临时节点ActiveStandbyElectorLock、持久节点ActiveBreadCrumb
-
但是如果ActiveStandbyElector在异常的状态下关闭Zookeeper Session,那么持久节点ActiveBreadCrumb会保留下来(此时有可能由于active NameNode与ZooKeeper通信不畅导致,所以此NameNode还处于active状态)
-
当另一个NameNode要由standy变成active状态时,会发现上一个Active NameNode遗留下来的ActiveBreadCrumb节点,那么会回调ZKFailoverController的方法对旧的Active NameNode进行fencing
①首先ZKFC会尝试调用旧Active NameNode的HAServiceProtocol RPC接口的transitionToStandby方法,看能否将其状态切换为Standby
②如果transitionToStandby方法切换状态失败,那么就需要执行Hadoop自带的隔离措施,Hadoop目前主要提供两种隔离措施: sshfence:SSH to the Active NameNode and kill the process; shellfence:run an arbitrary shell command to fence the Active NameNode
③只有成功地fencing之后,选主成功的ActiveStandbyElector才会回调ZKFC的becomeActive方法transitionToActive将对应的NameNode切换为Active,开始对外提供服务。
-
ZooKeeper之攘其外
ZooKeeper集群架构图
-
ZooKeeper集群也是主从架构
-
主角色:leader
-
从角色:follower或observer;统称为learner
-
客户端与ZK集群交互,主要分读写两大类操作
读操作
-
常见的读取操作,如ls /查看目录;get /zktest查询ZNode数据
-
读操作
-
客户端先与某个ZK服务器建立Session
-
然后,直接从此ZK服务器读取数据,并返回客户端即可
-
关闭Session
-
写操作
客户端写操作
-
①客户端向zk集群写入数据,如create /kkb;与一个follower建立Session连接,从节点follower01
-
②follower将写请求转发给leader
-
③leader收到消息后,发出proposal提案(创建/kkb),每个follower先记录下要创建/kkb
-
④超过半数quorum(包括leader自己)同意提案,则leader提交commit提案,leader本地创建/kkb节点ZNode
-
⑤leader通知所有follower,也commit提案;follower各自在本地创建/kkb
-
⑥follower01响应client
ZooKeeper之安其内
架构问题
-
leader很重要?
-
如果没有leader怎么办?
-
开始选举新的leader
-
-
ZooKeeper服务器四种状态:
-
looking:服务器处于寻找Leader群首的状态
-
leading:服务器作为群首时的状态
-
following:服务器作为follower跟随者时的状态
-
observing:服务器作为观察者时的状态
-
leader选举分两种情况
-
全新集群leader选举
-
非全新集群leader选举
全新集群leader选举
-
以3台机器组成的ZooKeeper集群为例
-
原则:集群中过半数(多数派quorum)Server启动后,才能选举出Leader;
-
此处quorum数是多少?3/2+1=2
-
即quorum=集群服务器数除以2,再加1
-
-
理解leader选举前,先了解几个概念
-
选举过程中,每个server需发出投票;投票信息vote信息结构为(sid, zxid)
全新集群,server1~3初始投票信息分别为:
server1 -> (1, 0) server2 -> (2, 0) server3 -> (3, 0)
-
leader选举公式:
server1 vote信息 (sid1,zxid1)
server2 vote信息 (sid2,zxid2)
①zxid大的server胜出;
②若zxid相等,再根据判断sid判断,sid大的胜出
-
选举leader流程:
假设按照ZK1、ZK2、ZK3的依次启动
-
启动ZK1后,投票给自己,vote信息(1,0),没有过半数,选举不出leader
-
再启动ZK2;ZK1和ZK2票投给自己及其他服务器;ZK1的投票为(1, 0),ZK2的投票为(2, 0)
-
处理投票。每个server将收到的多个投票做处理
-
如ZK1上:ZK1投给自己的票(1,0)与ZK2传过来的票(2,0)比较;
-
利用leader选举公式,因为zxid都为0,相等;所以判断sid最大值;2>1;(2,0)胜出;ZK1更新自己的投票为(2, 0)
-
ZK2也是如此逻辑,ZK2更新自己的投票为(2,0)
-
-
再次发起投票
-
ZK1、ZK2上的投票都是(2,0)
-
发起投票后,ZK1上有一个自己的票(2,0)和一票来自ZK2的票(2,0),这两票都选ZK2为leader
-
ZK2上有一个自己的票(2,0)和一票来自ZK1的票(2,0),这两票都选ZK2为leader
-
统计投票。server统计投票信息,是否有半数server投同一个服务器为leader;
-
ZK2当选2票;多数
-
-
改变服务器状态。确定Leader后,各服务器更新自己的状态
-
更改ZK2状态从looking到leading,为Leader
-
更改ZK1状态从looking到following,为Follower
-
-
-
当K3启动时,发现已有Leader,不再选举,直接从LOOKING改为FOLLOWING
非全新集群leader选举
ZAB算法
仲裁quorum
-
什么是仲裁quorum?
-
发起proposal时,只要多数派同意,即可生效
-
-
为什么要仲裁?
-
多数据派不需要所有的服务器都响应,proposal就能生效
-
且能提高集群的响应速度
-
-
quorum数如何选择?
-
集群节点数 / 2 + 1
-
如3节点的集群:quorum数=3/2+1=2
-
网络分区、脑裂
-
网络分区:网络通信故障,集群被分成了2部分
-
脑裂:
-
原leader处于一个分区;
-
另外一个分区选举出新的leader
-
集群出现2个leader
-
ZAB算法
PAXOS算法 -> RAFT算法 -> ZAB算法
-
ZAB与RAFT相似,区别如下:
1、zab心跳从follower到leader;raft从leader到follower
2、zab任期叫epoch;raft叫term
-
一下以RAFT算法动图为例,分析ZAB算法
ZooKeeper工作原理
写操作流程图
-
在Client向Follwer发出一个写的请求
-
Follwer把请求发送给Leader
-
Leader接收到以后开始发起投票并通知Follwer进行投票
-
Follwer把投票结果发送给Leader
-
Leader将结果汇总,如果多数同意,则开始写入同时把写入操作通知给Follwer,然后commit
-
Follwer把请求结果返回给Client
ZooKeeper状态同步
完成leader选举后,zk就进入ZooKeeper之间状态同步过程
-
leader构建NEWLEADER封包,包含leader中最大的zxid值;广播给其它follower
-
follower收到后,如果自己的最大zxid小于leader的,则需要与leader状态同步;否则不需要
-
leader给需要同步的每个follower创建LearnerHandler线程,负责数据同步请求
-
leader主线程等待LearnHandler线程处理结果
-
只有多数follower完成同步,leader才开始对外服务,响应写请求
-
LearnerHandler线程处理逻辑
-
接收follower封包FOLLOWERINFO,包含此follower最大zxid(代称f-max-zxid)
-
f-max-zxid与leader最大zxid(代称l-max-zxid)比较
-
若相等,说明当前follower是最新的
-
另外,若在判断期间,有没有新提交的proposal
-
如果有,那么会发送DIFF封包将有差异的数据同步过去.同时将follower没有的数据逐个发送COMMIT封包给follower要求记录下来.
-
如果follower数据id更大,那么会发送TRUNC封包告知截除多余数据.
-
如果这一阶段内没有提交的提议值,直接发送SNAP封包将快照同步发送给follower.
-
-
以上消息完毕之后,发送UPTODATE封包告知follower当前数据就是最新的了
-
再次发送NEWLEADER封包宣称自己是leader,等待follower的响应.
-
此博文仅供学习参考,如有错误欢迎指正。
上一篇《大数据-Hive(六)》
下一篇《大数据-Zookeeper(二)》