我们之前说了,Zookeeper 集群是由一个领导者(Leader
)和多个追随者(Follower
)组成,但这个领导者是怎么选出来的呢?我们貌似没有在配置文件中看到有关领导者和追随者的参数啊?
在此之前先来看看 Zookeeper 内部的一些机制:
那么领导者到底是怎么选出来的呢?很简单,每台服务器都有一个 id
(这里的 id
后面说),当启动的服务器超过半数的时候,就会选择 id
最大的 Server 成为领导者。比如有五台服务器,半数就是
2.5
2.5
2.5,因此当启动三台的时候就可以选出领导者。至于剩余的两台,启动之后只能成为追随者,因为领导者已经选出来了。关于这里的细节,一会儿再详细聊。
那么怎么指定服务器的 id
呢?还记得配置文件中的 dataDir
参数吗,在该参数指定的目录下创建一个 myid
文件(文件必须叫这个名字),然后在里面写上服务器的 id
即可。
[root@satori zkData]# echo 2 > myid
这里给 id
设置为 2,因为一会要搭建由三个节点组成的集群,而我希望当前节点成为 Leader,所以它的 id 应该为 2,其它的两个节点的 id
显然分别为 1 和 3。这样按着 id
从小到大的顺序启动时,该节点就会成为 Leader。
下面来我们来搭建 zookeeper 集群,总共三个节点:
82.157.146.194
,主机名:satori
121.37.165.252
,主机名:koishi
123.60.7.226
,主机名:marisa
satori
节点就是当前一直在用的节点,剩余的两个节点的 Zookeeper 也已经安装完毕。那么问题来了,我们要如何将这三个节点组成一个集群呢?显然还需要修改配置文件,先在 satori
节点进行修改。
# koishi 节点
server.1=121.37.165.252:2888:3888
# satori 节点
server.2=0.0.0.0:2888:3888
# marisa 节点
server.3=123.60.7.226:2888:3888
将集群中都有哪些节点写在 zoo.cfg
中,解释一下具体含义,首先两个冒号把等号右边分成了三部分,第一部分就不用说了,IP 地址或者主机名,用于定位节点;
2888
2888
2888 是 Leader 和 Follower 交换信息 的端口,因为副本要进行同步;
3888
3888
3888 是 交换选举信息 的端口,因为要选出 Leader。
然后我们注意到 satori
节点的 IP 设置成了 0.0.0.0
,这是因为当前的三个节点不在同一个网段,IP 用的都是公网 IP,而公网 IP 在绑定服务的时候会失败。所以在绑定的时候,其它节点的 IP 要写成公网 IP,自身节点的 IP 要写成 0.0.0.0
。因此其它两个节点的 zoo.cfg
文件就应该这么改:
########## koishi 节点配置 ##########
# koishi 节点
server.1=0.0.0.0:2888:3888
# satori 节点
server.2=82.157.146.194:2888:3888
# marisa 节点
server.3=123.60.7.226:2888:3888
########## marisa 节点配置 ##########
# koishi 节点
server.1=121.37.165.252:2888:3888
# satori 节点
server.2=82.157.146.194:2888:3888
# marisa 节点
server.3=0.0.0.0:2888:3888
但是在 生产中,一个集群内的节点应该都位于同一网段,然后将配置文件中的 IP 全部换成内网 IP 即可。这样彼此之间可以通过内网访问,而内网的访问速度要远远快于公网,并且还不需要走公网的流量。但我当前的三台云服务器不在同一个网段,所以只能用公网 IP,并且绑定的时候,将节点自身的 IP 换成 0.0.0.0
。
至于等号左边的 server.
是固定的,后面的数字表示节点的 id
,而节点 id
我们说了,通过在 myid
文件中进行指定。而节点 id
决定了,最终由谁担任领导者。其中 satori
节点的 id
为
2
2
2,刚刚已经改过了,然后将 koishi
和 marisa
两个节点的 id
分别改为
1
1
1 和
3
3
3,然后就大功告成了。
然后我们来启动 Zookeeper,由于 satori
节点的 Zookeeper 已经启动了,我们在修改完配置文件之后,需要重新启动。
但是我们查看状态的时候,发现出错了,相信原因很好想。因为配置文件中指定了三个节点,而剩余两个节点的 Zookeeper 还没启动。下面我们来启动一下,然后再次查看状态。
当剩余的两个节点启动之后,再次查看状态,发现 Mode 变成了 Leader。显然集群已经启动成功,至于剩余的两个节点,显然就是 Follower。
此时集群就启动成功了,但是关于领导者和追随者的选举问题,我们还得再说一说。
领导者选举分为两种情况:
我们先来看第一种情况,假设集群当中有 5 个节点,id
分别为 1 到 5,来看看选举过程是怎样的?这里 5 个节点按照 id
从小到大顺序启动。
LOOKING
;id
比自己大,于是会将自己的票改投给 server2。此时 server1 有
0
0
0 票,server2 有
2
2
2 票,但仍然没有哪个节点拥有超过半数的票,选举无法完成,server1 和 server2 状态都保持为 LOOKING
;id
都没有 server3 大,于是都会将票改投给 server3。此时 server1 和 server2 的票数为
0
0
0,server3 的票数为
3
3
3,由于 server3 的票数已超过半数,所以成功当选为 Leader,状态变为 LEADING
。而 server1、server2 则成为 Follower,状态改为 FOLLOWING
。FOLLOWING
。所以整个过程,关键点有两个:
server
启动之后都会发起选举,并将票投给自己。然后交换选票信息,并将票投给 id
最大的 server
。server
,不论 id
多大,也只能成为 Follower。以上就是集群第一次启动的时候,选举领导者。
但如果在运行过程中,领导者挂了该怎么办呢?显然要再选举出一个新的领导者。所以当集群中的追随者发现自己连接不上领导者的时候,就会开始进入 Leader 选举,但此时是存在两种可能的。
先来解释第二种情况,server5 认为 server3 挂了之后,便会发起 Leader 选举,呼吁其它追随者进行投票。但是其它追随者发现领导者并没有挂,于是会拒绝 server5 的选举申请,并告知它当前已存在的领导者信息。对于 server5 而言,只需要和已存在的领导者重新建立连接,并进行数据同步即可。
server3:老子还没挂呢!!!😤
但如果是第一种情况,领导者真的挂了,该怎么办?比如这里的领导者 server3,在运行的时候,节点突然宕机了。
要解释这个问题,我们需要引入一些新的概念。
sid
:就是我们一直说的服务器 id
,用于唯一标识集群中的节点。zxid
:事务 id
,客户端在发起一次写请求的时候,都会带有 zxid
,用于标识一次服务器状态的变更。所以 Zookeeper 也是有事务的,保证每次写数据的时候,要么全部写完,要么不写,不会出现只写一半的情况。另外每个节点都有自己的 zxid
,它们的值也不一定相同。epoch
:Leader 任期的编号,就好比古代皇帝,每个皇帝在当政的时候都有自己的年号。并且每投完一次票,这个编号就会增加。现在假设 server3 挂了,那么要重新选举 Leader,而选举规则如下:
epoch
,epoch
大的直接当选。epoch
相同,再比较 zxid
,zxid
大的当选。epoch
和 zxid
都相同,则比较 sid
,sid
大的当选。关于这么做背后的原理,我们先暂且不表,等到后面介绍 Paxos 协议的时候再细说。而且这里的 epoch
具体是干什么用的,估计也有人不太清楚,这些我们也留到后面再说。