手绘raft算法

在现实的分布式系统中,不能可能保证集群中的每一台机器都是100%可用可靠的,集群中的任何机器都可能发生宕机、网络连接等问题导致集群中的某个节点不可用,这样,那个节点的数据就有可能和集群不一致,所以需要有一种机制,来保证在大多数机器都存在的情况下向外提供可靠的数据服务。这里的大多数节点指的是集群半数以上的节点。

raft算法就是一种在分布式系统中解决集群中多节点之间数据一致性的算法。Golang生态圈中大名鼎鼎的etcd就是使用的raft算法来保持数据一致性的,与raft类似的一致性算法还有Paxos算法、Zab协议等。

其实,raft算法维持数据一致性的核心思想很简单,就是:“少数服从多数”。

leader选举

保证数据一致性,最好的方式就是只有唯一的一个节点,唯一的这个节点读,唯一的这个节点写,这样数据肯定是一致的;但是分布式架构显然不可以一个节点,于是,raft算法提出,在集群的所有节点中,需要有一个节点来充当这一个唯一的节点,在一段时间内,只有这一个节点负责读写数据,然后其他节点同步数据。这个唯一的节点叫leader节点,其他负责同步数据的节点叫做follower节点。在集群中,还会有其他状态的节点,例如candidate节点,这种节点只有在选举leader的时候才会有。
节点的leader选举和现实生活中的选举十分类似,就是投票,集群中获票数最多的那个,就是leader节点,所以为防止出现平局的情况(平局的情况也有解决方案,下文会说),一般在部署节点的时候,会将节点数设置为奇数(2n + 1)。
这些节点是如何选举的呢?我们先从followerleadercandidate这三种状态说起。
在集群中,有三个节点 A 、B、C。

image

在集群刚刚开始的时候,他们仨都是follower

image

过一段时间后,A变成了Candidate,这是要选举了!

image

为啥A能变成Candidate?凭啥?因为A的election timeout到期了,election timeout是选举超时时间,集群中的每个节点都有一个election timeout,每个节点的election timeout都是150ms ~ 300ms之间的一个随机数。每当节点的election timeout时间到了,就会触发节点变为candidate状态。A的选举超时时间到了,所以A理所当然变为了Candidate
所以,我们知道,其实A、B、C三个节点除了有状态,还有个选举超时时间election timeout

image

此时,candidate节点A会向整个集群发起选举投票,它会先投自己一票,然后告诉B、C 大选开始了!

image

注意!只有candidate状态的节点,才可以参加竞选变为leader,B、C这两个follower是没有资格的!
除此之外,每个节点中还有一个字段,叫term意思就是任期,和美国大选的第几期总统差不多一个意思,这个term是一个全局的、连续递增的整数,每进行一次选举,term就会加一,如果candidate赢得选举,它会当leader直到此次任期结束。
此时,A触发了选举,它的term应该是加一的。

image

当B、C收到A发出的大选消息后,B、C开始投票,此时只有A这一个candidate,所以理所当然发消息都只能投A。

image

此时A当选leader!
为了巩固自己的“统治”,防止A在任期之间其他节点因为自身election timout而触发选举,leader节点A会不定时的向两个follower节点B、C发送心跳消息,B和C收到心跳消息后,会重置election timout。心跳检测时间很短,要远远小于选举超时时间election timout

image

B、C收到心跳检测后,返回心跳响应,并重置超时时间election timeout

image

假设A发送的心跳检测消息由于网络原因例如延迟、丢包等等没有传送到B、C中的某个Follower节点,而此时这个节点刚好election timeout,则触发选举。
C修改自身节点任期值term为2,自身状态变为candidate,且投自身一票后,发起选举!

image

这时候,由于C的任期值term变为2大于A的,在raft协议中,但收到任期值大于自身的节点,都会更改自身节点的term值,并切换为Follower状态并重置election time
因此,这时候A由leader直接变为Follower

image

我们再来考虑一种极端情况:假设有偶数个节点,并且同时有两个节点进入candiate状态!
例如有以下四个节点A、B、C、D。A和B同时进入了cadidate状态并开始选举。

image

假如A和B中任意一个获得了超过半数以上的多数票,则变为leader!

image

但是假如两个经过一次选举后得的票数相同或者都没有超过半数,则宣告选举失败并结束!等待A和C这两个candidate节点中任意一个节点的election time超时,然后发起新一轮选举。
注意:虽然票数相同或者都没有超过半数导致的选举失败了,但是任期值term还是要叠加的!
A、B票数相同,等待哪个先超时。

image

此时A先超时。则A发起选举,由于Aterm值显然是最大的,则A会最终当选为leader

image

日志复制

leader选出来后,无论读和写都会由leader节点来处理。
是的,读也由leader来处理,leader拿到请求后,再决定由哪一个节点来处理,要么将请求分发,要么自己处理;即使client端请求的是follower节点,Follower节点也会现将请求信息转给leader,再由leader决定由哪个节点来处理。

下面来说说写的情况:
以下有A、B、C三个节点,其中A是leader节点

image

当client请求过来要求写操作的时候,leader A先把数据写在本身节点的log文件中

image

然后A将发append entries消息发送给B、C节点。
注意!append entries消息其实是根据节点的不同而消息也不同的,因为集群中数据可能不一致,一味的传相同数据,显然不可以。具体怎么不一致,稍后再说。
[图片上传失败...(image-faec91-1554608213608)]

B、C再收到消息后,把数据添加到本地,然后向A发消息,告诉A已经收到。

image

leaderA收到后,先提交记录,然后返回客户端响应。

image

然后,leaderA继续向B、C两个follower发送写数据commit的通知。

image

B、C两个节点收到通知后,先commit自身的log数据,然后再通知leaderA已更新结束。

image

到此整个数据同步也就结束了。
每次写数据,都需要先更新,然后commit。每个节点中都有两个索引,一个是当前提交的索引值commitIndex,一个是目前数据的最后一行索引值lastApplied。

image

而leader节点中,除了需要存储自身节点的commitIndex和lastApplied之外,还需要知道所有Follower的存储情况,因而leader节点中多了一张表,这张表中记录了所有follower节点的存储情况,这张表中有两个属性,一个属性叫nextIndex记录的是Follower节点没有的数据索引,需要发送append entries的数据索引;还有一个matchIndex记录的是leader节点已知的,follower节点的数据。如下图所示:

image

因此,当数据更新的时候,leaderA 向节点B、C发送不同的append entries

image

当A节点不再当leader时,其他节点并不能知道leaderA保存的matchIndexnextIndex这两个数组的数据。当其他节点成功当选为leader节点后,会将nextIndex全部重置为自身的commitIndex,而matchIndex则全部重置为0。如下图:

image

则,leaderB会向A和C节点发append entries,去"补全"数据

image

节点收到请求后,如果存在数据,就不动直接返回,如果没有数据则缺哪个补哪个。

总结

  • 触发选举的唯一条件是election timeout,心跳超时等其他条件仅仅是触发了非leader节点的election timeout
  • 节点选举的时候,term值大的一定会力压term值小的当选leader。
  • leader节点向follower节点中发送append entries的时候,并不是缺少1、2、3就直接发送1、2、3而是分批次,一次发送一条。1! 2! 3!三条数据,分三次发完。(怕图片误导,特此说明!)

更多精彩内容,请关注我的微信公众号 互联网技术窝 或者加微信共同探讨交流:

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

推荐阅读更多精彩内容