【Zookeeper系列】Leader选举机制

在之前的【Zookeeper系列】基本介绍里有提到 ZK 的角色,那篇文章只是简单介绍 LeaderFollowerObserver 这三种角色。那么在一个 ZK 集群中,我怎么知道 ZK 服务是哪一个角色呢?角色是怎么分配的?为什么某个 ZK 是 Follower,而不是 Leader ?结论先行,先简单回答上面的问题。

  1. 我怎么知道 ZK 服务是哪一个角色呢? 可以在 ZK 安装包的 bin路径下执行:zkServer.sh status,可以很清楚的看到打印出来Mode对应的角色,有可能是 leader,follower 或 observer。

  2. 角色是怎么分配的?为什么某个 ZK 是 Follower,而不是 Leader ? 通过 ZK 内部的选举机制,其细节可继续往下看这篇文章...
    [ps.为了降低复杂度,本篇文章并不会深入聊zab协议]

前置内容:

ZXID:当发生写请求的时候,在 ZK 内会发起一个事务,每个事务会分配一个 zxid作为标识符。zxid 是64位的 long 类型,高32位代表时间戳,低32位可以认为是事务ID,用来标识服务器状态的变更次数

SID:服务器ID。用来标识一台在 ZooKeeper 集群中的机器,每台机器不能重复。注意:这里和启动时指定的myid一致

Epoch:每个 Leader 任期的代号。每次新选举一个 Leader 就会递增1

Observer:不参与投票和选举【换句话说,不参与 Leader 的选举,也不会投票给某个节点】

节点状态:

  • LEADING:Leader节点的状态

  • LOOKING:参与选举的状态

  • FOLLOWING:Follower节点的状态

  • OBSERVING:Observer节点的状态

好了,前置知识大概这么多,接下来咱们看一下,上面这些东西和选举有什么关系。

选举目的是为了从一堆 ZK 的服务节点中找一个大佬(Leader),然后所有非 Observer 节点都会成为小弟(Follower)。Leader负责读写数据,而 Follower 会分担大佬的负担,分担部分的读请求。

那么问题来了,根据什么条件判断,认为某一台 ZK 服务能成为 Leader 呢?

实际上,选举的规则是按照 Epoch > 事务Id > SID 依次由大到小排序,值最大作为 Leader。

简单来说,选举的时候会比较epoch的大小,如果所有 ZK 节点的 Leader 任期大小一样,则继续比较 ZXID 的低32位,也就是事务Id大小,如果事务ID大小都一样的话,就会比较服务器ID(SID)

ps. 由于我是基于 Docker 部署 ZK 集群,进入其中的ZK节点,执行 cat /data/version-2/currentEpoch既可以看到epoch的大小

root@zoo3:/# cat /data/version-2/currentEpoch
5

ZK 集群选举

接下来,看下 ZK 集群是怎样选举 Leader 的,但值得注意的是,选举区分了第一次启动和非第一次启动。第一次选举流程比较简单,但非第一次选举的时候,会涉及到 Leader 宕机或假死的情况,这样的话细节就会多一些。

背景:ZK集群是由5台ZK节点组成,只要集群中过半的节点投票就可选出 Leader

第一次启动

在集群节点启动之初,所有的epoch都是一样的,此时 Leader 还没选举出来,此时不会有写请求进来,所以事务Id也是没有的,所以最终的判断条件是根据SID,也就是节点的ZOO_MY_ID决定,理论上来说,ZOO_MY_ID 值最大的就会成为Leader,但为什么说是理论上呢?咱们来看下实际的效果。

场景1:

基于docker-compose一次性部署5台ZK节点,参考我另外一篇博客【Zookeeper系列】基于docker-compose快速搭建Zookeeper集群

最终结果是例子中的zoo5成为Leader,符合上面 ZOO_MY_ID 值最大的就会成为 Leader 的说法。

场景2:

先把5台节点都关掉【执行docker-compose stop,模拟集群的节点准备启动的状态】,然后依次执行

docker start zoo1

docker start zoo2

docker start zoo3

集群中有5台节点,先启动3台,符合过半投票的情况,这时候已经可以选举出 Leader,盲猜一下,应该是 zoo3 这台机器成为Leader 了吧?进入 zoo3 容器,执行 zkServer.sh status 查看节点角色。果然,此时的 zoo3 是 Leader

此时,再依次执行 docker start zoo4 和 docker start zoo5,可以发现,Leader依旧是 zoo3。这是因为,当成功选举出 Leader 后,后续启动的 zk 节点都会成为 Follower(现在先不讨论Observer的情况)。

但这种结果并不与选举规则冲突,当 zk 集群内的机器不是同一时刻启动的时候,其大致选举流程是:

  1. 先启动 zoo1 ,zoo1会先给自己投票,但由于票数没有过半,所以 zoo1 此时是 LOOKING 状态;

  2. 接着启动 zoo2,zoo2一开始也会给自己投票,然后与 zoo1 交换投票信息。zoo1会发现当前的 epoch 和 zxid 都与 zoo2一样,但 zoo2 的 Sid 比自己大,zoo1 就会改票,将自己的那一票给了 zoo2,此时 zoo2 虽然有 zoo1 的投票支持,但投票票数还是没超过一半,不能成为Leader,zoo2 也保持 LOOKING 状态;

  3. 再启动 zoo3,同样的 zoo3 也会先给自己投票后,再和 zoo1、zoo2 交换投票信息。zoo1 和 zoo2 都发现与 zoo3 的 epoch 和 zxid 一样大小,但 zoo3 的 Sid 比自己高,所以 zoo1 和 zoo2 都改票投给了 zoo3,此时 zoo3 有了3张票(包括自己自投),超过半数,所以 zoo3 就成为了 LEADING 状态,zoo1 和 zoo2 成为了 FOLLOWING 状态。

  4. 接着启动 zoo4 和 zoo5,此时 zoo1、zoo2 和 zoo3 节点都不是 LOOKING 状态,不会交换投票的信息,发现 zoo3 是 LEADING 状态,所以 zoo4 和 zoo5 都投票给 zoo3,并更改自己的状态为 FOLLOWING 状态

但为什么在场景1的时候,不是 zoo3 成为 Leader 而是 zoo5 成为 Leader 呢?这是因为在近乎同一时刻,zk 集群所有的服务都启动了,此时所有节点都是先投票给自己,然后再与其他节点交换信息,发现 zoo5 的 Sid 最大,接着所有的节点都投票给了 zoo5 ,其余节点就都处于 FOLLOWING 状态,而 zoo5 是 LEADING 状态。

非第一次启动

场景1:

假设 zoo3 是 Leader,其余都是 Follower。当 zoo3 宕机,其余节点都变为 FOLLOWING 状态,重新参与选举。

为降低复杂度,将真实的 zxid 简化为只有事务Id。假设 zoo1、zoo2、zoo4、zoo5 的 (epoch,zxid,sid) 分别是 (1,13,1),(1,10,2),(1,13,4),(1,12,5)

此时选举流程是怎样呢?

  1. 首先存活节点发现大佬没了,就会将自己的状态更改为 LOOKING 状态,此时所有节点都会给自己投票,然后与其他节点交换投票信息;此时发现大家的epoch一样,则比较彼此的 zxid

  2. 很明显,zxid 最大的是 zoo1 和 zoo4,再继续比较彼此的 sid

  3. 此时可以轻易得出,zoo4 的 sid 最大,就会将 zoo4 的状态更改为 LEADING。其余节点投票给 zoo4,并更改状态为 FOLLOWING

场景2:

zoo3 为 LEADING 状态,此时 zoo3 假死。如果 zoo3 忽然网络出问题,断开与其他 follower 节点的连接。其他节点以为 zoo3 宕机,则重新选举新 Leader,假设此时 zoo1 成为大佬,此时 zoo3 忽然正常了,那么剩下的 follower 节点会不会蒙圈?怎么会有两个大佬,正所谓一山不能容二虎,我应该听谁?

实际上,zk 有考虑到这种情况,还记得上面说的 epoch 的定义吗?这是每个 Leader 的代号,每更新一次 Leader,epoch 就会递增1。假如 zoo3 假死前,所有的节点的 epoch 都是2。zoo3 假死,zoo3 的 epoch 不会变,因为没想到有其他节点想替代自己的位置嘛。但其他节点选举 zoo1 为 Leader 后,除了 zoo3 ,其他的节点的 epoch 都是3了。此时 zoo3 恢复正常,即使向其他节点同步事务消息,但其余节点发现 zoo3 的 epoch 和自己不一样,就不会认这个大佬,而是认准 zoo1 才是自己的老大。

总结

实际上,zk 集群的选举并不简单,底层选举算法使用到的 ZAB 协议保证分布式消息一致性,本篇文章并没过多描述。对于初学者来说,了解其选举的规则和某些场景下是如何选举,大概了解流程足矣。当实际开发中,为了保证高可用,需要注意的是 ZK 集群节点为奇数。另外,感兴趣的读者可以再去关注 ZAB 协议的细节。

参考资料:

《从Paxos到Zookeeper分布式一致性原理与实践》

如果觉得文章不错的话,麻烦点个赞哈,你的鼓励就是我的动力!对于文章有哪里不清楚或者有误的地方,欢迎在评论区留言~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容