本文将详细解释如何分析ZooKeeper集群。文章内容质量较高,边肖将分享给大家参考。希望你看完这篇文章后有所了解。
一、为什么需要集群
1.1 马果果病了
ZKr~ ~老规矩~
毕竟马国老了,这个办公室的东西越来越多。终于有一天,她受不了了。她生病住院了。医生让她休息几天。没有办事处的负责人,村民就不能得到服务。大家平时要注意自己的健康~
坤坤急得好几天没接到通知,和其他非常依赖办公室的村民一起去村委会投诉。村委会也很无奈。最后,经过讨论,决定请村里同样有很高威望的知名企业家、池发烧友、村里首富设立第二个办公室,这个办公室离原来的办公室也很近,也负责处理以前马办公室的事务。马小云以前也是办公室的常客,他非常了解这个过程。他一直想为人民做实事,他欣然同意。
况且我们已经有了之前的成功经验,直接复制之前的处理流程就可以了。然而,细心的村民很快发现了问题。马办公室里记录的许多事情现在都消失了。在新办公室重新注册很不方便,但马小云说他无能为力。马病得太突然了,还没交出来,只能说明大家都能承受。
就这样,两个星期后,马康复出院了。在住院期间,他还了解到,过去两周,马小云帮助村民解决了大大小小的事务。他非常感谢马小云所做的一切。他热爱自己的工作,并尽快回到工作岗位。他重新打开办公室的门,还通过广播告诉村民,他可以再次在这里办事。希望大家能继续来这里。毕竟马小云的业务能力有点差,处理速度也没那么快,导致二处排队比较严重。
听到第一间办公室重新开放,村民们非常高兴。毕竟没有人愿意在排长队中浪费时间,所以他们都来到了第一办公室。
不过,马休息了两个星期。在这两周内,他没有为村民进行任何登记。村民们都说这样不行:“我们不管你有多少间办公室,你要保证数据一致!”。村委会也同意了村民的要求,责令两个办事处整改,需要解决!而且,由于马国年纪较大,经验更丰富,所以让马小云听从马国的指挥,而这个方案也是由马国想办法引进的。
1.2 马果果的新规定
芒果不愧姜还是老的辣,很快就想出了一个好主意,介绍了一系列规则:
数据必须基于芒果。
这两个办公室需要保持联系,时刻保持联系。
以前把村民登记的事务分为读书和写字是非常正确的决定。之后,写操作必须经过Magog,读操作马小云可以自己解决。
然而,仅仅发布规则是不够的,还需要一系列可以实施的操作。于是马曹保果向村委会提出申请,村委会办公室需要招收更多的学生。村委会决定让马放手,同意了他的申请。
马国重新调整了自己办公室的布局,变成了这样:
简单介绍一下新同事:
PS负责区分村民的请求是否需要发起提案,并将请求再次转发给S和C。
小C负责管理小PS提案的提交。这个职位很重要,所以马就自顾自地邀请了一个女生来担任这个职位。
现在小s不再和小F打交道,而是和打交道,等他建档后会通知。
现在有两个办公室,我们需要雇一个接线员与隔壁的马小云办公室联系。
光安排自己是不够的。马还为设计了一个新的办公计划:
和芒果不太一样,但是这里也有
简单介绍下:
-
使用小FR替换了原来的小P作为办事处第一接待人
-
小S也不和小F打交道了,直接和小SA打交道,等他归档完就会通知小SA
-
和马果果一样也聘请了一个话务员负责和马果果进行联系
原来只有马果果负责的一个办事处,随着马果果的病倒,村民的业务就无法继续展开了,这就是单点故障,所以在原来的基础上增加一个办事处,可以增加整个办事处的吞吐量的同时也可以在一个办事处无法提供服务时,不至于导致村民们无法使用,这就是高可用。这也是为什么需要集群部署的最重要原因!
二、第一办事处
引入了集群前,原本一个节点数据自己内部运作管理就行,非常方便,但是引入集群后,集群间的节点如何沟通成了问题,让我们一起来看看马果果的新员工们是怎么做的吧?
不同于之前的单机流程,现在流程复杂了很多,增加了很多出场的人物,为了让大家能快速记忆,我这里提前把名字的由来剧透给大家:
-
小P对应代码中的 PrepRequestProcessor 负责预处理
-
小PS对应代码中的 ProposalRequestProcessor 负责写事务的提案
-
小C对应代码中的 CommitProcessor 负责对事务请求提交
-
小S对应代码中的 SyncRequestProcessor 负责数据的归档
-
小A对应代码中的 AckRequestProcessor 负责告诉马果果当前事务的 ACK 信息
-
小F对应代码中的 FinalRequestProcessor 负责对内存模型的操作
2.1 负责的小PS
原先小P在第一时间询问村民后,并对当次请求进行标记后,就会把该请求转发给小PS,小PS做的事情也很简单:
主要就是看是不是写请求,如果是的话就要发起提案并且本地要通知小S归档。
2.2 忙碌的小C
小C是除了小F最忙碌的人了,她在接受到上一个同事传递过来的请求后会:
不是说小C是最忙的吗?就这?
别急,小C的处理过程的确是比较繁琐,但是我这里先给出简单的流程,最重要的提交操作,我暂时不展开,之后会讲~
2.3 小S和小A
小S处理的流程发生了改变,他前面的同事不再是小P,而他处理完归档后也不再把请求交给小F而是交给小A,而小A做的事情更简单,仅仅只是告诉马果果办事处此次事务请求归档成功,其实就是 ACK。
2.4 话务员
为了更顺畅的和隔壁的马小云办事处相互沟通,马果果定下了几个暗号,而话务员则负责用暗号去通知马小云
-
REQUEST
-
PROPOSAL
-
ACK
-
COMMIT
当然暗号不止这些,之后有遇到再说。
在具体展开流程细节前,我觉得还是要把马小云的流程简单介绍下,等两边都介绍完后,再合并在一起讲解~
三、第二办事处
同样因为现在有两个办事处的关系,马小云也无法单纯使用之前的流程,并且新员工中有明显区别于马果果的小FR和小SA,这里也介绍下:
-
小FR对应代码中的 FollowerRequestProcessor 负责马小云这边的预处理
-
小SA对应代码中的 SendAckRequestProcessor 和马果果的小A类似,通过话务员通知马果果当前事务的 ACK
3.1 同样细心的小FR和小SA
和小PS有点类似,也是需要区分读写,但区别是写请求需要通知马果果。
小SA的逻辑是接受到小S的归档信息后,把 ACK 通知给马果果,太简单了就不画图了。
四、实战
刚刚我们把两个办事处逻辑都大致介绍了下,但是太过于碎片化了和简单,所以下面开始进入实战环节,会分别假定不同的业务场景和复杂程度,从简单到复杂,把从村民来办事处登记事务到办事处处理完成之间的逻辑按照时间顺序进行整理。
前排提醒,多图预警
4.1 一个读请求(马果果)
假设我们的坤坤来到马果果的办事处,想要查询鸡太美最新的跳舞视频 /鸡太美/跳舞
小P首先知道坤坤是合法的村民,然后询问得知,此次来办事处的目的是为了查询,就会把此次登记标记为读请求,就把坤坤的请求交给下一个柜台的小PS。
小PS拿到请求后,先把请求原封不动的给到了小C,之后通过小P的标记知道了这是一个读请求,便不做其他处理。
小C取到这个请求后也发现这是一个读请求,所以也直接交给了小F,自己不需要其他处理。
小F拿出了小红本查看,假设 /鸡太美/跳舞 存在,把对应的数据就返回给了坤坤。
坤坤拿到了结果心满意足的回去了并且定好了 17 点的闹钟守在电脑前等着鸡太美的开播了
可以看到一个读请求的处理流程是非常简单的,别急,难度会一点点的增加哦
4.2 一个读请求(马小云)
同样还是我们的坤坤,但是这次来到马小云的办事处,同样想要查询鸡太美最新的跳舞视频/鸡太美/跳舞
与马果果不同的是,先处理坤坤请求的是小FR,他会先把请求发给小C,之后通过询问坤坤得知此次目的是查询,就不会做其他处理。你可能会问,小FR不需要对坤坤的身份进行核实吗?我认为可能是因为当前是读请求所以不会对数据造成破坏,所以并没有做校验。
之后的小C和小F和马果果版本没有任何不同,就不赘述了。让我们进入下一个难度吧。
4.3 一个写请求(马果果)
写请求就和读请求不一样了,因为根据马果果的规定,两个办事处的数据得保持一致,所以就会涉及到如何通知对方了,让我们一起来看看吧。
假设我们的坤坤来到马果果的办事处,想要为自己创建一个事务登记 /坤坤/日记
小P知道坤坤是合法的村民并且坤坤此次的目的是写数据,所以就给坤坤的请求打了一个写事务的标记,就把请求交给了小PS了。
小PS还是先把请求交给了小C先处理。
小C看到此次是写请求就拿出自己的小本子记了下来
小PS已经得知此次是写请求。注意!这里开始就不一样了,小PS会让话务员给马小云办事处打电话。
话务员告诉他们这次的请求并带着 PROPOSAL 的暗号。
这里必须要提一下事务编号,为了严格保证村民来登记的顺序,马果果还规定了必须给每一次的写事务分配一个唯一的递增数字,从 0 开始。
并且通知马小云的同时,马果果也会把当前的提案记录下来:
这时候我们把视角切换到马小云这边,马小云的话务员接受到马果果那边的 PROPOSAL 的暗号后,会直接让自己这边的小S进行归档,马小云则会在备忘录里记录:
等小S归档完后,就会把坤坤的请求交给小SA
小SA事情很简单就是让话务员通知马果果归档完成
接着马小云这边的话务员就会给马果果办事处打电话通知他们归档完成
视角再一次回到马果果这边,在小PS让话务员通知马小云那边的同时小S也没闲着,进行了归档的操作
小S归档完成后,会把请求交给小A,小A做的事情很简单就是通知马果果此次归档完成。
我们这里假设先是马果果这边的小S归档完成,马果果在收到归档完成消息后会拿出刚刚的小本本找到对应的提案记录,并把已经归档完成的给记下来:
因为马果果知道一共有两个办事处,所以还需要等待马小云的归档完成通知。
过了一会会,马小云的归档通知也来了,就再在小本本上记下来
至此,两个办事处对于当前提案都已经完成了归档,马果果就会让话务员通知马小云可以提交了,并且会将小本本上事务 0 的这条记录删除(图就不画了)。
通知完,马果果就让小C可以进行提交了
小C就会拿出刚刚的备忘录,找到坤坤的等待处理的事务的第一条(当前场景只有一条)就是:创建 /坤坤/日记。就马上把这个事务交给了小F处理
小F就会在小红本上把当前事务记录下来:
交给小F后,小C发现坤坤的所有事务都处理完了,就把他从备忘录上删除了:
让我们把视角再切到马小云,马果果这边的小C在处理的时候,马小云的话务员收到了来自马果果的 COMMIT 消息并告诉了马小云,而马小云会从备忘录中找出最早的一条请求就是:坤坤,创建,/坤坤/日记,然后就会把该请求交给小C
至于之后小C处理以及处理完交给小F处理,和马果果那边的逻辑是一样的,就不赘述了。
至此,一个写请求(马果果)的基本流程就算完成了。
4.4 一个写请求(马小云)
假设我们的坤坤这次来到马小云的办事处,同样为自己创建一个事务登记 /坤坤/日记
小FR先把请求交给了小C,然后发现了坤坤这次来办理的是写请求,就会要求话务员通知马果果。
话务员就会打电话给马果果办事处,并且告诉他们此次的请求以及携带上 REQUEST 暗号
而马小云这边的小C会和之前的例子一样,也会在备忘录里记录下
现在我们把视角切到马果果这边,话务员接受到 REQUEST 的请求后,告诉了马果果,而马果果会直接把这个请求交给小P去处理
仿佛就是坤坤直接来自己办事处办理业务一样,从小P之后的流程和之前的例子可以说是一模一样了,就不赘述了。
我现在举了 4 种无并发的场景,除了写请求都很简单,我这里就再把写请求马果果重新用图画一遍
为了简约图中省略了故事中的话务员以及马果果和马小云,相同颜色代表处于同一时间处理,时间顺序从小到大。
好了,让我们继续提升难度进入并发实战,作为一个公共的办事处是不可能同时只处理一件事务的!
4.5 多个村民多种请求
这一章节我不会再从头开始画图了,只画重要的部分,我们先看看,如果有多个村民的话,那几个小本本是怎么记的吧!
小C的备忘录的特点总结:
-
以村民作为 key,之后的请求是按照请求的顺序摆放的队列
-
队列中的第一个请求肯定是写请求,如果是读请求的话,就根本不会记录
-
当村民对应的请求队列为空后,整条记录删除
我们以图中的坤坤举例,假设现在等待的请求是这样的(我省略了路径,这里只关心事务的类型)
当第一个创建的请求入队后,之后的查询请求也无法被执行,都需要等到创建请求执行完毕后才能继续,所以当第一个创建的请求被提交后,之后的查询1、查询2、查询3会被立马按顺序移除出队列并执行,而查询4则需要等待前面的删除和创建全部提交后才会被执行。
这样的逻辑保证了,同一个客户端的请求是按照时间顺序执行的,不会出现后到的读请求先于前面的写请求执行,造成脏读,但是需要注意的是不同的客户端的顺序是无法保证的,很可能坤坤的创建请求还未提交,之后东东的查询操作就能被返回了。
马果果的小本本如果有多条记录的话就是这样
你可能会问:鸡太美的那条记录不是归档完成了吗,为什么还在小本本里?因为 ZK 必须保证事务执行的顺序!所以只要有比当前事务编号小的其他事务仍然未提交,本事务就不能提交,图里就是鸡太美必须等到坤坤和东东都提交完才能进行提交!
马小云这边也有一个备忘录,如果有多条记录的话会是这样:
这个备忘录其实是一个先进先出的队列,每次马小云的提交会从队列中移除最前面的一条记录来操作。
故事差不多讲完了,有些细节用程序员的语言再说一下,我其实省略了两个处理器 :
-
ToBeAppliedRequestProcessor这个处理器在马果果这边才是紧接着小C的,但是我个人感觉下来没什么用就不讲了,大家有兴趣可以去了解下
-
LeaderRequestProcessor 这个处理器才是马果果的第一个处理器,他的逻辑涉及到会话、ACL,其他就没什么用,留到之后有机会讲
大家先看下这个图:
用红框标记的都是线程对象,主要逻辑都在 run 方法中,小P和小S我们之前就讲过了,这里就多了小C和小FR。这里我得提一下,但凡你们只要在 ZK 中看到线程对象,那么他基本上是使用了生产者-消费者的模型,对象内部维护了一个阻塞队列,我这次就不画图了,因为重要的逻辑之前都已经讲了。
画了这么多图,这里先进行下小结:
-
马果果就是我们平时在 ZK 中听到的 Leader 节点,负责对写事务请求发起提案并最终决定提交
-
马小云对应就是 ZK 中的 Follower,只能独自处理读请求,写请求需要转发给 Leader 去处理
-
读请求无论客户端请求给哪个服务端的节点,处理流程都相对简单,可能几乎不需要节点之间的通信,自己就能处理(前提是没有待处理的写请求)
-
写请求如果客户端请求到的是 Leader, Leader 就会对此次事务请求在集群中发起提案,接受到提案的 Follower 各自进行归档,并返回给 Leader 成功的 ACK 信息,Leader 对 ACK 进行统计,达到集群数量半数以上就集群中发起 COMMIT 请求,Follower 们接收到提交请求后,才会修改各自内存中的数据
-
写请求如果客户端请求到的是 Follower,Follower 在本地做简单记录后就会把请求转发给 Leader 去处理,之后和上一条是一样的情况
-
分布式事务的理论中是有回滚阶段的,当集群中的节点本地提交失败后,会通知 Leader 失败信息,而 Leader 统计 ACK 之后发现本次事务无法提交就会发送回滚的请求给各个节点。但是!很遗憾我并没有在 ZK 的源码中找到和事务回滚有关的逻辑(当然也很有可能是我疏忽了,如果你知道逻辑在哪儿的话,请一定告诉我),我现在的认为小S处理归档的时候是不允许失败的,如果失败报错了,整个服务节点会报错退出
五、剧透
在本篇文章的最后,我们再看看动物村又发生了哪些故事吧。
马果果毕竟年事已高,需要定期去医院进行体检,就会耽误办事处这边的工作,而渐渐的随着时间的推移,马小云的业务能力越来越强了,心里就产生了:凭什么我要听这老头的?于是主动向村委会提议,建议 Leader 的人选要进行选举,不能就一直让马果果占着这个位子,村委会听后也觉得很有道理,于是拉来马果果一起商量,马果果肯定不能同意啊,但是无奈马小云毕竟是首富,很快就通过上下打点让村委会一致通过了这个建议
但是现在只有两个办事处,如果双方各执一词,就平票了,所以村委会再次经过商量决定引入第三个办事处,这个新办事处的负责人选择了村里的著名企业家马小腾,这样就不会出现平票的情况,具体选举规则如下:
-
每天三个办事处开张前必须要先投票选出一个 Leader
-
Leader 必须是经手处理过事务编号最大的
-
需要各个办事处半数以上的同意
-
一切以 Leader 为主,读写请求遵守之前马果果的定下的流程
-
当 Leader 无法处理事务的时候,需要立即选出新的 Leader,选出之前办事处不能对外提供服务
所以现在有了三个办事处成了这样
关于怎样分析ZooKeeper 集群就分享到这里了,希望
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/157340.html