zookeeper作为一个分布式协调服务,一定是要满足扩展性,可靠性,时序性的。
并且作为一个分布式环境中的协调者,它的速度将非常影响整体的速度,因此它也必然需要是快速的。
扩展性
基本上,每一个分布式应用的架构体系的构成都会有角色的概念。
zookeeper也是如此,它有三个角色:
Leader
Follower
Observer
之前我们已经提及过了leader和follower,那observer又是什么呢?
这是zookeeper在基于主从复制下将读写分离发挥到最大化的小心思。
怎么说?
其实observer和follower差不太多,都是作为一个“从”的角色,只能接受读请求,而不能承担写请求。
但区别是:
follower其实有点备机的意思,它会在主机宕机后,参与选举,然后某一台follower就将会成为新的leader。也就是说follower有可能成为leader的机会。
observer不能参与选举,而只是会在新的leader选举出来之后,进行随从。也就是说它就是很彻底的贯彻作为从机的使命,为读写分离的快速发挥最大化的作用,observer永远没有可能成为leader。
我们还应该考虑一个成本问题,请问3个人和1000个人中采用过半选举的机制,哪个会选的更快?
自然是3个人的。
因此,在我们选择zookeeper角色搭配的时候,我们应该尽可能的少布置一些follower,
因为越多可能选举会越慢。剩余的节点除了leader可以都设置为observer角色。
配置Observer
只需要再zoo.cfg中对要设置为observer的节点,后面加上“:observer”,例如对节点4置为observer:
server.1=node01:2888:3888
server.2=node02:2888:3888
server.3=node03:2888:3888
server.4=node04:2888:3888:observer
可靠性
zookeeper如何确保一致性?
攘其外必先安其内
安其内
也就是说想要对外是可靠的,那么先要保证自身系统内部是可靠的。
zookeeper本身就是一个多节点分布式的,当系统内部发生内乱时(leader宕机),必须要快速的能够重新选出新的leader,国不可一日无君,如果自身不能做到快速稳定内乱,又如何保证协调他人安康?
但要注意,当zookeeper的存活节点不足过半条件的时候,整个服务对外将不可用。也就是如果有3台,只能挂1台,如果挂2台就不行了。 5台只能挂2台,挂3台就不行了。
关于选举的细节,可参考我写的第三课。
攘其外
当自身系统是正常运作的时候,就要保证对外的服务是好用的,也就是能够保证外人使用zookeeper是操作安全的,说白了就是zookeeper要保证自身各节点操作数据的一致性。
zookeeper的一致性级别是选用的最终一致性。
那具体是怎么实施的呢?
paxos
这里,我们需要先了解一下paxos,可以先参考一篇关于paxos的文章:
https://www.douban.com/note/208430424/
Paxos,它是一个基于消息传递的一致性算法,Leslie Lamport在1990年提出,近几年被广泛应用于分布式计算中,Google的Chubby,Apache的Zookeeper都是基于它的理论来实现的,Paxos还被认为是到目前为止唯一的分布式一致性算法,其它的算法都是Paxos的改进或简化。有个问题要提一下,Paxos有一个前提:没有拜占庭将军问题。就是说Paxos只有在一个可信的计算环境中才能成立,这个环境是不会被入侵所破坏的。
其中,zookeeper使用paxos可以看下是怎么实施的:
情况一:
屁民甲(Client)到某个议员(ZK Server)那里询问(Get)某条法令的情况(ZNode的数据),议员毫不犹豫的拿出他的记事本(local storage),查阅法令并告诉他结果,同时声明:我的数据不一定是最新的。你想要最新的数据?没问题,等着,等我找总统Sync一下再告诉你。(因为最终一致性,因此client访问zookeeper的某个节点的数据不一定是最新的,如果需要得到最新的,需要主动让该节点去同步一下主节点)
情况二:
屁民乙(Client)到某个议员(ZK Server)那里要求政府归还欠他的一万元钱,议员让他在办公室等着,自己将问题反映给了总统,总统询问所有议员的意见,多数议员表示欠屁民的钱一定要还,于是总统发表声明,从国库中拿出一万元还债,国库总资产由100万变成99万。屁民乙拿到钱回去了(Client函数返回)。(反应了一次数据修改的过程,follower会将写请求转交给leader,然后leader需要基于paxos协议与过半的节点达成一致,然后返回客户端本次操作成功)
情况三:
总统突然挂了,议员接二连三的发现联系不上总统,于是各自发表声明,推选新的总统,总统大选期间政府停业,拒绝屁民的请求。(zookeeper当leader宕机后,其余节点将进入选举模式,不对外提供服务)
ZAB(Zookeeper Atomic Broadcast) 原子广播协议
zookeeper为了保证简单快速,使用的并不是paxos算法,而是基于paxos算法的简化版ZAB。
ZAB的特点:
原子:成功、失败。没有中间状态
广播:zookeeper是分布式多节点的。需要广播给各节点,然后需要过半返回OK。
队列:FIFO,顺序性。 因为所有的写请求会最终转发给leader,因此leader内部为每个follower都各自维护了一个队列,用来记录将要发送给follower要去做的事情,这个指令都是有序的。
ZAB是两阶段提交的。zk的数据状态在内存,但会用磁盘保存日志,所以第一次会先用日志记录,第二次再更新内存。
我们以2台follower,1台leader为例。
我们来讲解一下当一个客户端想要创建一条数据时的具体流程:
1、client向某台follower发起create的请求。
2、follower发现是一个写请求,自己处理不了,将请求继续转发给自己附属的leader。
3、leader会基于当前的事务id号进行递增1。
4、leader会先在自己本地日志为本次创建的数据的操作写上一条记录。
5、leader会分别在自己对应的每个follower的队列里追加一条征求确认创建数据的操作请求(leader征求follower意见:我想创建条数据,你同不同意啊?)。队列里的指令会按照顺序执行。
6、一台follower接到leader的征求,并在自己本地日志为本次创建的数据的操作写上一条记录。然后回复leader一个同意确认(我同意创建这条数据)。
7、当leader收到一台follower的确认答复后,因为leader自身其实算是支持的一票,再加上follower同意的一票,此时3台里有2台同意了,因此leader会再次向自己对应的每个follower队列里追加一条写数据的指令(leader说:我看大部分人都同意本次的操作了,我宣布本次创建数据的操作选举通过,大家把数据去存一下吧)
8、leader等待之前过半确认的follower节点在其自身内存写入成功后,都返回ok。
9、leader回复客户端响应。
这里注意到,我们只提及到一个follower,而没有去理会另外一个follower。
注意第7步的时候,另外一个follower此时可能还没有回复客户端确认。
而我们因为过半同意,leader又向两个follower各自的队列里积压了一条操作。
所以,其实另外一个follower只需要一步步去执行里面积压的操作,最终也是可以完成数据的修改的。
这就是所谓的最终一致性。
而最终一致性,有可能会出现一种现象:
另外一个客户端与此同时去访问另外一个follower的时候,如果直接get数据,可能得到的不是最新的(因为还没有同步完成)。
但可以解决:可以在请求该follower的时候,要求follower主动发起sync,同步一下leader的数据,再返回给客户端(一般可能是在sync的操作里写入回调函数:进行get数据操作)