第一章 Zookeeper的架构原理和使用
第 3 节
Zookeeper架构原理的浅尝就止(下)
上一节提到了Zookeeper集群可以支撑数据的顺序一致性,高可用性。也简单的提到了是通过2PC的ZAB协议保证的。那么今天就来简单的看下ZAB协议是个什么鬼?具体是怎么保证的这些的呢。
基于ZAB协议选举、主从同步、崩溃恢复原理
为了保证数据一致性和高可用,Zookeeper使用了一种特殊的协议来保证的。大家应该已经都知道了,就是Leader+Follower的角色划分+ZAB协议实现的。
先简单介绍下吧,ZAB协议全称,ZooKeeper Atomic Broadcast,就是ZooKeeper原子广播协议的意思。是ZooKeeper用来在集群之间同步数据的一种协议。它是Paxos算法的变体,也是两阶段提交思想的一种实现。这一点大家不要弄混了。
Zookeeper是怎么使用ZAB协议的呢?其实从启动到主从同步,崩溃恢复这些阶段或者场景中,都是在ZAB协议的要求下进行的。
而且这些阶段中,ZAB协议会处于两种模式下,恢复模式和消息广播模式。恢复模式主要根据选举算法选举出Leader,消息广播模式主要保障数据同步一致性。
首先我们来看下Zookeeper启动阶段
Zookeeper启动阶段,处于恢复模式,主要进行的是Leader选举。因为在ZAB协议中,必须有角色划分Leader+Follower,才可以进行。所以必须有一个选举算法来选举出一台Zookeeper作为Leader。
怎么选举呢?其实很简单,就和生活中的选举选拔一样的方式—投票,少数服从多数。而选举是由策略或者标准的,需要根据一些信息,选取最优的一个。比如你去中国好声音去参赛,评委会根据你的表现等信息,决策是否选你。
其实你会发现,很多选举都是一样的。它们根本的逻辑就是,需要获取信息->决策->得到结果。
带着这个思想你去看Zookeeper的选举,就很简单了。当你第一次了解一个东西的时候,你千万不要一开始就陷入细节,这样很容易走偏。而是应该先摸下脉络,在看细节,这样重复的多了你就会发现类似的东西,会发现规律。渐渐的就会穿过现象,看透本质了。这个思想非常重要,你一定要注意这一点。
需要获取信息,Zookeeper的实现是,设计了这样3个信息元素,如下图所示:
知道这个关键的三个信息之后,就可以通过某种选举流程进行选举了,最终得到结果。分析leader的选择机制,zookeeper提供了三种选举方式,LeaderElection 、AuthFastLeaderElection、FastLeaderElection (默认)。如下图所示。
好了,你知道了Zookeeper启动阶段,进入恢复模式,帮ZAB协议选举出了一个Leader。接着就是进行主从数据同步阶段了。
这其实就是一个两阶段提交的实现。第一次proposal请求算是确认网络联通,准备提交阶段,超过一半的写成了,就进行第二次Commit请求,才是实际提交事务的阶段
也就是说,当写操作发生在Leader的ZooKeeper上后,leader会分配一个全局唯一递增的事务ID(zxid)。
Leader收到写的请求,会认为是一种事务请求。首先ZooKeeper会将写操作转换为事务Proposal(提议),通过一个独立队列顺序的同步给每个的Follower。Follower需要立即写入本地磁盘日志中,写入成功之后就可以保证数据不会丢失,然后返回一个ack给leader。如下图所示:
当集群中超过半数的Follower都确认收到事务proposal请求,Leader就会再给所有的Follower发一个Commit请求,让所有Follower提交这个事务请求。leader自己也会进行commit操作。如下图所示:
只要集群宕机不超过一半的机器,就可以重新选举新的leader。选举过程和启动是的选举是类似的。
但是有几种情况你可以好好思考下:
情况1:如果一个leader自己刚把一个proposal发给部分follower,没有超过一半的人返回ack,leader是不会将proposal写入本地磁盘日志的,此时宕机.
重新选举即可,因为之前的那条写proposal根本不算成功。
情况2:如果一个leader自己刚把一个proposal写入本地磁盘日志,没来得及发送给全部的follower,就宕机了。
此时新leader选举出来,它的epoch会自增长一位。老leader恢复了连接到集群就会变成是follower了。此时发现自己比新leader多出来一条proposal,但是自己的epoch比新leader的epoch低了,所以就会丢弃掉这条数据了。
情况3:如果一个leader把一个proposal写入本地磁盘日志后,发送了一部分commit,突然宕机了。
如果接受commit的follower没有一个成功,肯定没什么影响。但是如果有任意一台成功,此时新leader选举出来的一定是zxid较大的,也就是有commit的那一台,数据比较新,之后其他follower按照最新的同步一份数据过来即可。
持久化机制
Zookeeper除了内存数据,磁盘数据一般分为快照和事务日志。
快照(snapshot):记录了整个内存中的数据,即datatree的Node数据,这个过程一般是异步的。
事务日志(log):实时记录了所有访问zookeeper的事务请求,包括create、setdata、setacl、delete等等操作;
你如果类比下其他中间件,几乎所有中间件持久化都是类似的,都增量操作的日志和快照两种。比如redis的aof和rdb。hdfs的fsimage和editLog。
你可以想下,这两个文件的作用是什么?肯定是为了保障数据不丢,保障可以快速恢复数据。
Zookeeper的持久化,你可以简单得到,下图所示:
日志和快照不可能无限大,无限多。所以Zookeeper会创建一个异步线程,定时清理无用文件。
这里面有很多细节实现,之后研究源码原理时,会带大家一一来看的。你知道它持久化有这两个东西就行
Watcher监听回调机制
最后我还要强调Zookeeper非常重要的一个机制—Watcher监听回调机制。这里先简单介绍下它是个什么意思。
Watcher监听回调机制,说白了就是客户端告诉服务端它关系的Znode数据,当数据发生变更,服务端主动要通知客户端,然后客户端做一些事情的过程。按照这个理解,这个过程主要就是三步,如下图所示:
至于具体如何注册和触发,以及回调的,这里暂时不做展开,之后会详细分析的。
小结
我们来小结下《Zookeeper架构原理的浅尝就止》上和下,这两节的内容。
上一节:
Zookeeper集群可以支撑什么:顺序一致性、高可用、高性能和小集群下的高并发读写。Zookeeper集群适合读多写少的场景。
内存数据结构和节点角色划分:内存使用Znode树形数据结构维护数据,节点主要有Leader、Follower、Observer三种角色。
Zookeeper集群通信机制:客户端和服务端通信使用了Java原生的NIO的API,服务端和服务端之间使用了Java原生的BIO的API。
这一节:
基于ZAB协议选举、主从同步、崩溃恢复原理:ZAB协议是Paxos的变体,2PC的一种实现。在Zookeeper启动进入恢复模式,根据zxid,serverId等信息进行选举出Leader,之后进入消息广播模式,进入主从同步状态,在ZAB协议规定下,通过2pc+过半写的机制保障数据一致性。最后如果崩溃的话,可以再次进入恢复模式,重新选举Leader。
持久化机制:快照文件和事务日志,快照文件是异步生成的,事务日志是实时顺序写入的。持久化文件达到限制大小,会不断创建新文件,当然也会定时清除无用的文件,并且崩溃时可以根据持久化文件恢复数据。
Watcher监听回调机制:客户端告诉服务端它关系的Znode数据,当数据发生变更,服务端主动要通知客户端,然后客户端做一些事情的过程。主要分为了三步客户端注册Watcher、服务端Watcher触发、客户端执行Watcher的回调。
上面这些内容其实涉及了Zookeeper最核心的一些原理了,你可能一时间记不住这么多。没关系的,之后我们会一步一步掰开了,揉碎了,仔细分析他们的。当你分析过,思考过后,相信你渐渐地就会掌握的。
好了,到这里通过之前篇章,初步的带大家对Zookeeper的架构原理进行了快速学习或者回顾,我们浅尝就止,大家先有个大致印象就可以了。甚至这些够你应付一些面试问题了。起码考察Zookeeper的时候,从技术广度上讲,你是了解的了。至于技术深度的提升,就需要和我一起步入第二章一起探索了。
下一节,会给大家简单介绍下Zookeeper的部署,核心参数。接着就会进入第二章,带着大家探索Zookeeper的源码世界了,请大家敬请期待吧!
PS:由于工作平常比较忙的原因,写文章的时间可能不稳定,但每周会保证2更的。
金句甜点
今天的甜点是,不忘初心,找回斗志和单纯。
有些时候当你走着走着,你会发现一件事:生活会教育你,教育的你不是你自己了。比如你的斗志很容易被人夺走。虽然古人说过,三军可夺帅,但不可夺其志。但是很多时候,当你经历了很多风吹雨打,你以为你变的成熟了,变的圆滑了。但是很多时候是你失去了斗志了。再也没有吹牛过自己了,你会说:“我这是变的低调了”。但是其实你甚至没有高调过。因为高调过的人,才说可以自己低调。你可能都是趴在地下的,已经低调的再不能低了,还低什么低了,是不是?
过去的我们很单纯,但是现在反而我们变的复杂了,走着走着,很多时候掺杂了自己的东西。我们应该越学越简单,越学越单纯,千万不要越学越复杂。其实成功很简单,是我们把它搞复杂了,道理很简单,是我们把它想复杂了。怎么越来越单纯呢,跟上“疯子”的“傻子”会成功。你要做一个傻子,不要给人感到你很聪明。成功的人其实很多开始时候是又傻又天真的。这种才更容易成功。
刚才我和你聊了聊斗志和单纯,为什么聊这个呢?是因为,你有时候会受到别人的指点,但更多的时候还是别人的指指点点点。正常来说,你要不在乎众人的指指点点,要在乎高人的指点。但是你经常会倒过来了,这个肯定不行。你可能特别在乎别人的指指点点,而对高人的指点置若罔闻。一路上,就是被别人的指指点点变的不是你自己了。到最后你活的不是你自己,是别人让你活成啥样,你就活成啥样了。邻居说怎么样的人才是好人,你变得像邻居说的一样。父母说你是怎样的就好,你变得父母喜欢的样子。你从来没有活成你喜欢的样子。这就是我们变成了周围人的样子。人生最大遗憾之一,就是活成了、变成别人喜欢的样子。你活成你自己的喜欢的样子,才一切都有可能。像“疯子”一样有斗志,像“傻子”一样有单纯。这两个你把它们找回来,你会活出另一个天地,另一个春天。
一个人没有了斗志是能看出来的,从你的眼神,单纯,是从你的表情,就比如小孩子们的表情,而不是大人们的一脸的狐疑。其实表情是最大的泄密武器。假如你在街上遇见一个算命的,他看到你表情不好,一下就能戳中你的心门,让你心甘情愿花钱算命。你应该知道,你和优秀的人在一起,你才更容易变成优秀的人。你肯定希望和成功的人,技术好的人在一起。但是你反过来想想,他们凭什么和你在一起,你没有斗志的眼神,你的表情不单纯。他们怎么指点你?你没有斗志,他们想着先怎么鼓励你,你一脸狐疑,指点的话没法给你说,因为你的表情告诉他们,你不信他们。那他们肯定只能给你说些温暖的话,善意的话,让你先不怀疑。最终相信不相信,可不是你看你嘴上怎么说的。这个下一节我们再聊,今天的金句甜点就先聊到这里吧。