写在前面
假定我们现在使用zk执行了如下的指令:
[zk: 192.168.0.10:2181(CONNECTED) 0] create /dongshidaddy 123
Created /dongshidaddy
[zk: 192.168.0.10:2181(CONNECTED) 1] create /dongshidaddy/mongo 456
Created /dongshidaddy/mongo
假定因为节点故障最终导致,指令create /dongshidaddy/mongo 456
先于指令create /dongshidaddy 123
执行,则就会报如下错误:
[zk: 192.168.0.10:2181(CONNECTED) 0] create /dongshidaddy/mongo 456
Node does not exist: /dongshidaddy/mongo
但很明显我们在使用zk的过程是没有遇到过这类问题的,为什么呢?因为zk使用了能够保证操作指令顺序性的分布式共识协议ZAB(zookeeper atomic broadcast)
,接下来我们就一起看下吧!
1:zab
我们直接通过一个例子来看下zab是如何保证指令执行的顺序性的,假定有一个三个节点,节点A,节点B,节点C的集群,如下图:
假定此时有指令X,Y需要执行,为了执行指令,需要先生成提案,提案的格式是事务标识符:指令
,其中事务标识符格式为(epoch,递增编号)
,其中在一个任期内递增编号是递增的,当有一个新的任期时递增编号从0开始,假定我们这里的的任期是1,X,Y的递增编号分别是1,2 ,则二者的提案分别为<1,1>:X
,<1,2>:Y
,如下图:
zab通过唯一的事务标识符,就能保证指令的操作顺序了,leader节点A收到客户端的指令生成提案,并将生成的提案发送给节点B和节点C,如下图:
当节点A收到了大多数节点的确认响应之后,将会提交提案,并通知节点B和节点C提交提案,如下图:
这里提交提案需要注意的是,如果是前面还有更小的提案没有被提交,这里为了保证指令的顺序性,将会等待前面的提案提交后才会真正的提交。
写在后面
小结
多知道一点
zab的第一个字母为什么是z(zookeeper)
因为zab协议是zk为了解决指令的顺序性问题而设计的一种分布式共识协议,所以用自己的名字来命名就很正常了。
zk为什么不采用raft
因为raft是在2013年才被提出的,而zk是2007年开发的,所以不是不用,而是没有。
zk是强一致性还是最终一致性
因为zk基于zab协议,也是基于大多数
的来更新数据的,且zk允许读请求通过从节点来执行,因此可能读到旧数据,所以是最终一致性,如果想要读到最新数据的话,可以通过执行sync指令来完成,如下:
[zk: 192.168.0.10:2181(CONNECTED) 2] sync /dongshidaddy/mongo
[zk: 192.168.0.10:2181(CONNECTED) 3] Sync returned 0
[zk: 192.168.0.10:2181(CONNECTED) 3] get /dongshidaddy/mongo
456
cZxid = 0x100000005
ctime = Mon Apr 20 21:19:28 HKT 2020
mZxid = 0x100000005
mtime = Mon Apr 20 21:19:28 HKT 2020
pZxid = 0x100000005
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
[zk: 192.168.0.10:2181(CONNECTED) 4]