本文作者:HelloGitHub-老荀
Hi,这里是 HelloGitHub 推出的 HelloZooKeeper 系列,收费开源、乏味、入门级的 ZooKeeper 教程,面向有编程根底的老手。
我的项目地址:https://github.com/HelloGitHu...
明天开始咱们将深刻 ZK 集群相干常识~
一、为什么须要集群
1.1 马果果病了
ZKr~老规矩~
马果果毕竟年纪大了,这办事处的事件越来越多,终于有一天扛不住,生病了,住院了,听医生说要劳动好几天。办事处负责人不在的话就不能给村民们提供服务了。大家平时也要留神身材啊~
一连好几天都没收到告诉的坤坤急死了,还有其余十分依赖办事处的村民们都一起跑去村委会投诉了,村委会也很无奈啊,最终磋商了下,决定请村里权威同样很高的驰名企业家,太极爱好者并且还是村里首富的马小云成立第二办事处,地点离原来的办事处也很近,同样负责解决之前马果果办事处的事务。马小云之前也是办事处的常客,对其中的流程曾经是十分分明了,始终想为人民做实事的他,怅然许可了下来。
而且曾经有了之前的成功经验,所以间接照搬之前的解决流程就行了。然而仔细的村民很快就发现了问题,之前在马果果的办事处很多曾经记录过的事务,当初都隐没了,在新的办事处这里须要从新注销,十分不不便,然而马小云示意也没方法,马果果病得太忽然了,还没交接过,只能示意让大家忍忍。
就这样,过了两周,马果果痊愈入院了,在住院期间他也曾经得悉了马小云这两周代他帮忙村民解决大小事务,他心田非常感激马小云所做的所有,热爱工作的他,第一工夫就回到了工作岗位,把办事处的大门从新关上,也播送通知了村民,本人这里又能办理事务了,心愿大家能够持续过去。因为马小云毕竟业务能力稍差点,处理速度没那么快,导致第二办事处排队更重大了。
听到第一办事处又倒闭的村民们非常高兴,毕竟谁也不心愿排长队浪费时间,于是都来到了第一办事处
然而呢,马果果劳动了两周,这两周期间村民的注销的事务,他全副都没有,村民们纷纷表示这不行啊:“咱们不论你们有几个办事处,你们得保证数据都是统一的啊!”。村委会也批准村民的诉求,勒令两个办事处整改,须要解决这个问题!而且因为马果果资格更老,更有教训,所以让马小云所有听马果果的指挥,计划也由马果果去想方法出台。
1.2 马果果的新规定
马果果不愧姜还是老的辣,很快就想出了一个好方法,出台了一系列的规定:
- 数据必须以马果果为主
- 两个办事处间须要买通分割,随时放弃沟通
- 之前把村民前来注销的事务辨别成读和写,是十分正确的决定。之后写操作必须通过马果果,读操作马小云能够自行解决
然而光出台规定还不够,还须要一系列能够落地的操作,于是马果果向村委会申请,办事处须要扩招人,村委会决定让马果果撒手干,批准了他的申请。
马果果把本人办公室的安排从新调整了下变成了这样:
简略介绍下新来的共事们:
- 小PS负责辨别村民的申请是否须要发动提案,并把申请再次转发给小C以及小S
- 小C负责管理小PS提案的提交工作,这个职位十分重要,所以马果果很有公心的请了一个妹子来承当这个职位
- 当初的小S不再和小F打交道了而是和小A打交道,等他归档完后就会告诉小A
- 因为当初有两个办事处了,所以须要延聘一个话务员,专门负责和隔壁的马小云办事处进行沟通
光是安顿好本人还不够,马果果替马小云也设计了一套新的办公室计划:
和马果果不太一样,这里也简略介绍下:
- 应用小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,选出之前办事处不能对外提供服务
所以当初有了三个办事处成了这样
所以下一篇的主题大家应该晓得了吧~就是:选举!会讲讲 ZK 集群是如何选举出 Leader。敬请期待吧~
老规矩,如果你有任何对文章中的疑难也能够是倡议或者是对 ZK 原理局部的疑难,欢送来仓库中提 issue 给咱们,或者来语雀话题探讨。
地址:https://www.yuque.com/kaixin1...
老哥们转评赞安顿一下好吗,ZKr~