大白话详解大数据ZooKeeper知识点,老刘真的很用心

435 阅读15分钟

 前言:老刘目前为明年校招而努力,写文章主要是想用大白话把自己复习的大数据知识点详细解释出来,拒绝照本宣科,做到有自己的理解!

01 ZooKeeper知识点

第1点:什么是ZooKeeper?

简而言之,Zookeeper是一个分布式协调框架,注意是协调框架,在kafka0.8中就会有一个问题,关于这个协同框架的,以后会说道,大家先注意下。它用来解决分布式系统中系统一致性的问题,利用Zookeeper可以实现Hadoop的高可用(高可用会在后面讲到),可以将kafka的offset保存在Zookeeper,但是这里会有一个问题,以后说到kafka会好好说说这个问题。

第2点:为什么要用Zookeeper?

这个问题,可以举个例子,在Hadoop的高可用中,如果不使用Zookeeper,那它两个NameNode怎么切换,就要自己写程序实现切换,这样就会导致注意力分散了,hadoop本身做存储之类的,现在还要我hadoop考虑高可用的逻辑,实在是太累了,能不能让别人分担下我的压力。所以就用Zookeeper来帮助Hadoop分担压力,让Hadoop集中注意力。

第3点:Zookeeper的一些命令行操作

# 启动ZooKeeper集群;在ZooKeeper集群中的每个节点执行此命令
zkServer.sh start

# 停止ZooKeeper集群(每个节点执行以下命令)
zkServer.sh stop

# 查看集群状态(每个节点执行此命令)
zkServer.sh status

# 连接ZooKeeper的服务器
zkCli.sh -server node01:2181,node02:2181,node03:2181

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

#创建节点,并指定数据
create /lao liu

#获得某节点的数据
get /lao

#修改节点的数据
set /lao liu1

#删除节点
delete /lao

第4点:在IDEA中进行JAVA API,这个要记住,千万不能忽略!

这个就不分享了,太多了,想要的话,直接联系老刘,老刘给你就是了。

第5点:ZooKeeper的组成成分

它有三个部分,一个是类似于文件系统的目录节点树,来存储数据,二是一些操作ZooKeeper的基本命令,三是它的监听器Watcher。

什么叫做类似于文件系统的目录节点树?

那什么是监听器?

这里就先简单讲讲监听器的一些概念,监听器它有三个部分,(1)注册:客户端向ZooKeeper集群注册监听器(2)监听事件:监听器负责监听一些的事件(3)回调函数:当监听器监听到事件的发生后,就会调用的回调函数。

在这里可以举个例子,就是在一些机构看到的,感觉比较经典,就拿出来给大家看看。

1 一哥们去酒店办理入住,但是被告知目前无空房
2 这哥们告诉客服:帮我留意一下有没有空出的房间,如果有,就及时通知我
(相当于注册监听器,监听一些事件)
3 将近12点,有房客退房,有空闲的房间(发现要监听的事件)
4 客服发现有空房(监听到了事件)
5 及时通知这哥们
6 这哥们收到通知后,马上赶回酒店(就相当于调用了回调函数)

第6点:ZooKeeper中的数据节点ZNode

ZNode分为4类:持久节点 临时节点 有序节点 非有序节点

主要讲讲有序节点

持久节点就相当于文件目录系统中的目录,而临时节点就是和session会话绑定在一起的,当客户端和ZooKeeper集群相连接,就相当于建立了一个会话,当这个连接断开,会话就失效,临时节点也失效,会被删除。

那为什么还会有有序节点呢?

这个就是为了防止多个客户端在同一个目录下,创建同名的ZNode,如果同名,就会报错,所以就有了有序节点,这有序节点在创建时,ZooKeeper会自动在这些节点后面追加上一个整型自增的数字。

第7点:什么是session会话呢?

客户端和ZooKeeper集群中某一个节点建立TCP长连接,就叫一个会话。在一个会话中,请求以FIFO(先进先出)的顺序执行,比如客户端那边先发出一个create请求后,又发出了一个get请求,那么在执行时,就会先执行create,再执行get。但是呢,如果有两个会话,那就没有办法保证两个会话间的FIFO了,它只能保证一个会话中的FIFO,这个老刘不知道原因,只是记下来了,哈哈哈!

第8点:什么是事务?

在ZooKeeper中的事务,老刘还没理解清楚,这里就不写了,大家自行百度,等以后哪天老刘突然想到了,再来弥补。

第9点:什么是Watcher?

老刘在第5点就简单说了下Watcher的例子,现在好好讲讲它的用处,首先问一个问题,客户端和ZooKeeper建立连接后,客户端是如何获取到Zookeeper中最新的数据的呢,它是如何感知到有数据变化的呢,那就是通过介绍的watcher,老刘在第5点举了一个watcher的例子,老刘觉得非常经典。通过那个例子,我们可以理解到watcher就是客户端在ZooKeeper服务器上注册的监听器,用它来监听数据节点ZNode上数据的变化,然后告诉客户端最新消息。

第10点:说一说ZooKeeper集群架构

首先呢,ZooKeeper集群是一个主从架构,在ZooKeeper集群中有三个角色:leader,follower,observer。那知道了这三个东西,必须要了解它们的概念。

leader,领导者,为客户端提供读写服务,维护集群状态。

follower,跟随者,为客户端提供读写服务,向leader汇报自己的状态信息,同时也要参与“过半写成功”和leader选举。

observer,观察者,它是特殊的follower,为客户端提供读写服务,向leader汇报自己的状态信息,但不参与“过半写成功”和leader选举。

那客户端是如何和ZooKeeper集群进行读操作的?

看到这张图,就会感觉到ZK的读操作很简单,就是客户端先和ZK集群中的某个服务器建立session,然后直接从这个ZK服务器读取数据,读完后返回到客户端,最后关闭session。就是这么简单!

那客户端是如何和ZK集群进行写操作的呢?

有没有人觉得也会很简单,老刘当初就是这样想的,当看完之后就发现自己还是太肤浅了,ZK的写操作比ZK的读操作要复杂好多好多。

先分享出自己看到的一个特别好的例子,这个例子在老刘看来真的非常形象的表达出了ZK写操作的过程。

1、就是有一个富豪来到银行,对一个柜台小姐姐说我昨天在这里存钱,你们少给我存了1000万,现在需要你们给我加进去。

2、那这么大的金额,这个柜台小姐姐肯定是没有权限进行操作的,她就会汇报给经理,那经理也不能随便加,他为了让自己的操作服众,他就会征求自己所有下属的意见。

3、如果大多数人都同意加,经理就会做出决定,同意此事。并且会告知所有下属,让他们记下这件事。

4、那么最开始的柜台小姐姐就会通知富豪操作成功,加了1000万。

看完这个例子,大家接下里再看ZK写操作,就会发现几乎一模一样。

① 客户端向ZK集群写入数据,例如create /test;与集群中的最左边的follower建立session会话。

② follower就会将写请求转发给leader。

③ leader收到消息后,就会发出proposal提案创建/test,然后通知每个follower先记下要创建/test。

④ 现在就开始进行投票,是否允许创建/test这个写操作。如果在这个集群中,有个超过**半数(quorum)**的人同意,也同意的人包括leader自己,这个部分会在后面详细介绍,那么leader就会commit提案,leader就会在本地创建ZNode节点/test。

⑤ 之后leader就会通知所有follower,也commit提案,在各自的本地创建ZNode节点/test。

⑥ 结束上述所有操作后,最左边的follower就会响应客户端。

怎么样,怎么样,是不是和举的例子非常相似,哈哈哈!

第11点:ZK集群中leader选举

leader选举分为两种选举,一种是全新选举,另一种是非全新选举。这里老刘就仔细讲讲全新选举,非全新选举和全新选举大致相同,大家可以去自行搜索。

在leader选举中,有个非常重要的原则,就是超过半数(quorum)Server启动后,才能选举leader。这个半数如何计算呢?举个例子,在3台机器组成的ZK集群中,半数等于3/2+1=2,就是集群服务器的个数除以2,再加上1。

在选举过程中,每个服务器都会投票,投票信息是这种结构(sid, zxid),在全新leader选举中,每个服务器的初始投票信息为server1-(1, 0),server2-(2, 0),server3-(3, 0)。

那究竟如何判断选举得到leader呢?就是server1投票(sid1, zxid1),server2投票(sid2, zxid2),就会进行比较,先比较zxid,谁大谁是leader;如果zxid相等,就会比较sid,sid谁大谁是leader。

上述基本讲完了leader选举中的知识点,接下里就让老刘详细说一遍leader选举步骤,还是在3台机器组成的ZK集群中讲这个选举。

先假设按照ZK1、ZK2、ZK3的顺序依次启动,那么半数就为2了。

1、启动ZK1后,投票给自己,vote信息(1,0),没有过半数,不能进行选举。

2、再启动ZK2;ZK1和ZK2票投给自己及其他服务器;ZK1的投票为(1, 0),ZK2的投票为(2, 0)。

3、现在集群个数达到2,就可以进行选举了,先开始处理投票。ZK1会把投给自己的票(1,0)与ZK2传过来的票(2,0)比较;利用leader选举公式,因为zxid都为0,相等;所以判断sid最大值;2>1;所以ZK1更新自己的投票为(2, 0)。同样的道理,ZK2也进行同样的逻辑,ZK2更新自己的投票为(2,0)。

4、处理完投票后,再次发起投票选举。现在ZK1、ZK2上的投票都是(2,0),那么ZK2就会被选为leader,接着就会更改服务器状态,更改ZK2为Leader;更改ZK1状态为Follower。

5、最后当K3启动时,它发现集群中已经有了Leader,就不会进行选举,直接变为follower。

第12点:仲裁qurorum知识点总结

老刘在这先说一句,ZAB算法,老刘以后再讲,现在还没搞清楚。

什么是仲裁?

发起proposal时,只要大多数派同意,就能够生效。

为什么要仲裁?

就是不需要所有的服务器都响应,proposal就能生效,提高了集群的响应速度,也比较合理。

quorum数如何选择?

在3台机器组成的ZK集群中,半数等于3/2+1=2,就是集群服务器的个数除以2,再加上1。

第13点:ZooKeeper工作原理

读写操作已经在第10点讲述,现在开始讲讲ZooKeeper状态同步。在完成leader选举之后,ZK就会进入ZooKeeper之间的状态同步。

那究竟是如何进行状态同步的呢?让老刘先把脑子里记得的东西写出来,不急!

1、leader会构建一个NEWLEADER封包,在这个NEWLEADER封包中包含着这个leader的最大zxid,然后广播给其他follower。

2、follower接收到后,就会用自己的最大的zxid进行比较,如果自己的最大zxid小于leader的,那就说明自己的数据不是最新的,需要和leader状态进行同步;否则不需要。

3、如果需要同步,那leader就会给需要同步的每个follower创建LearnerHandler线程,这个线程就会负责数据同步的请求。

4、leader主线程就会等待LearnHandler线程处理完结果。只有大多数follower完成同步时,leader才开始对外响应写的请求。

5、上述是大致的状态同步过程,但是在第4步仅仅简单描述了一下LearnerHandler线程,接下里就详细说一下在LearnerHandler线程的流程:

① 首先会接收follower的封包FOLLOWERINFO,包含这个follower的最大zxid。

② follower的最大zxid与leader最大zxid比较,若相等,说明当前follower是最新的;

③ 在判断期间,还要判断有没有新提交的proposal。如果有,就会发送DIFF封包将有差异的数据同步过去。同时将follower中没有的数据逐个发送COMMIT封包给follower保存下来;如果没有,但follower数据id更大,那么会发送TRUNC封包告知截除多余数据;如果follower的最大zxid比leader最大zxid小,就会直接发送SNAP封包将快照同步发送给follower。

④ 以上消息完毕之后,就会发送UPTODATE封包告知follower当前数据就是最新的了,就差不多了。

第14点:ZooKeeper实例之HDFS HA

讲了那么多ZooKeeper的原理,现在开始讲讲ZooKeeper的实例,免得面试官问ZooKeeper相关的实例,自己没有准备。这次主要讲讲HDFS HA,HDFS HA实现高可用主要就是依赖于ZooKeeper,它主要包括两部分:一个是元数据同步,一个是主备切换。

先讲讲元数据同步,主要是下图中的画红圈部分。

下面讲讲元数据同步的流程,在同一个HDFS集群中,运行两个NameNode节点。一个是主Namenode节点,处于Active状态,一个是从NameNode节点,处于Standby状态。其中只有Active NameNode能对外提供读写服务,Standby NameNode会根据Active NameNode的状态变化,在主节点出现异常的时候,就会切换成Active的状态。

但是在主备切换过程中,新的Active NameNode必须确保与原来的Active NamNode元数据同步完成,才能够对外提供服务。

那如何做到元数据同步呢?

这里就会用到JournalNode集群作为共享存储系统,当客户端对HDFS进行操作的时候,会在Active NameNode中edits.log文件中作日志记录,同时日志记录也会写入JournalNode集群,它负责存储HDFS新产生的元数据。当有新数据写入JournalNode集群时,Standby NameNode就能监听到这种情况,将新数据同步过来,那么Active NameNode和Standby NameNode就实现了元数据同步。另外,所有的datanode也会向两个主备namenode做block report。

现在就到了主备切换,好好讲讲!先画出流程图:

根据这个流程图,好好体会体会这个过程。

1、每个NameNode节点上都会有一个ZKFC进程,ZKFC进程它负责控制NameNode的主备切换。

2、ZKFC在启动时,同时会初始化HealthMonitor和ActiveStandbyElector服务,ZKFC同时也会向HealthMonitor和ActiveStandbyElector注册相应的回调方法,HealthMonitor监控NameNode的健康状态,ActiveStandbyElector会接收ZKFC的选举请求,创建一个临时节点ActiveStandbyElectorLock。

3、接下来,两个ZKFC就会通过各自ActiveStandbyElector会尝试在Zookeeper创建临时节点ActiveStandbyElectorLock,但由于Zookeeper的写一致性,就会导致最终只会有一个ActiveStandbyElector创建成功。

4、创建成功的 ActiveStandbyElector回调ZKFC的回调方法,将对应的NameNode切换为Active NameNode状态,而创建失败的ActiveStandbyElector回调ZKFC的回调方法,将对应的NameNode切换为Standby NameNode状态。

5、但是呢!不管是否选举成功,所有ActiveStandbyElector都会在临时节点ActiveStandbyElectorLock上注册一个Watcher监听器,来监听这个节点的状态变化情况。

6、如果Active NameNode对应的HealthMonitor检测到NameNode状态异常,就会通知对应的ZKFC。ZKFC会调用 ActiveStandbyElector 方法,删除在Zookeeper上创建的临时节点ActiveStandbyElectorLock。此时,Standby NameNode的ActiveStandbyElector注册的Watcher就会监听到这个删除事件。

7、收到这个事件后,此ActiveStandbyElector发起主备选举,成功创建临时节点ActiveStandbyElectorLock,如果创建成功,则Standby NameNode被选举为Active NameNode。

第15点:脑裂

什么是脑裂?

在分布式系统中出现两个leader的现象,就是脑裂。产生的原因有很多,例如网络有延迟之类的,这种情况的出现是非常可怕的,必须通过自带的**隔离(Fencing)**机制来避免这种现象。

那隔离到底是怎么做到隔离的呢?

1、ActiveStandbyElector成功创建ActiveStandbyElectorLock临时节点后,会创建另一个ActiveBreadCrumb持久节点,这个持久节点保存了Active NameNode的地址信息。

2、当Active NameNode在正常的状态下断开Session,会同时删除临时节点ActiveStandbyElectorLock、持久节点ActiveBreadCrumb。

3、但是如果ActiveStandbyElector在异常的状态下关闭Session,那么持久节点ActiveBreadCrumb会保留下来。

4、当另一个NameNode要由standy变成active状态时,就会发现上一个Active NameNode遗留下来的ActiveBreadCrumb节点,那么会回调ZKFC对旧的Active NameNode进行fencing。

02 总结

好啦,终于结束了!老刘关于大数据ZooKeeper知识点一共总结了15点,每一点在老刘看来都非常重要,老刘对他们进行了熟记,记在了脑子里,希望对想学大数据的你们有帮助,也希望能够得到大佬的批评和指点。