前言
接触了一些分布式相关的理论基础,并且也看了Raft的论文,是时候该总结一下了。
本篇文章主要聊了下面一些东西
- 分布式下为何要共识
- CAP理论
- 多数派原则
- Raft共识算法PartA:选举与心跳
分布式下为何需要共识
- 何为分布式?
一群Server共同工作,每个Server都是一台计算机,各个Server可以相互通信。也即:多个Server一起做多个事情。多个人干多个事。
-何为集群?
同一个服务,部署多个节点共同承担流量,这就是集群。一件事由多个人来干。
- 为什么要进行共识?
在日常生活中,我们与他人进行工作,是需要沟通协作的。共识算法就是一种让分布式下的几个Server对某种操作达成共识。
为什么需要区分主从节点?
分布式下,数据一致性保证是很重要的。所有的Server对一条数据,应当达成一种共识状态。而主从架构,则是最直观的数据一致性保证方式。(现实也是,无论组织什么活动,都有一个发起人来统一管理,这样是最有效的)。如果不存在数据一致性的要求,也没必要采用主从架构咯。
从这张图中,可以讨论很多有趣的问题。
CAP 理论
CAP理论探讨的是,设计分布式架构的时候,总是在Consistent(一致性) 、Availablity(可用性) 和 Partition Tolerance(分区容错性) 之间做取舍。分布式系统最重要的一点,当发生了网络分区的时候,服务依旧是可用的,也就是说P是必要的。
具体的CAP详细分析可看我上一篇文章:各注册中心对比以及简单原理分析
你可能注意到,我在Master向Follower进行同步的时候,他们的请求的顺序是一样 的。原因是,这个过程是并行向All Follower发送同步请求,异步接收成功通知(这取决于网络/或处理速度)。那么在向Client回复Success的时候,这个请求必然已经被同步到了Follower。
接下来的问题是:万一有服务断开连接了怎么办?(断电、地震、死机......)Client应该收到Success的回复吗?
当Follower1挂了,从而导致整个服务瘫痪不可用,你自己觉得可以这样干吗?当然不行啦。还有两台机留着浪费呢???
强行进行CP是不太现实的,因此人们提出了一种折中的方式,那就是允许数据存在短暂的不一致的情况。下面则更像是对于CP的延展。
多数派原则
多数派原则 不仅仅是现实生活中一种公平的手段,也是分布式中很重要的原则。一旦这个同步请求,接收到了多数派Follower的成功回复,那么Master就会回复Client,请求成功。
这也是一个问题的答案:为什么集群节点都是奇数节点而不是偶数节点?
如果一个集群中服务挂了大多数,那么这个服务就是不可用了。
假设现在有5台机器,大多数则是2台机器。 他可以允许两台同时故障。那么如果变成6台机器,他的大多数是4,他也只运行挂两台。所以,都是采用奇数节点的方式。
什么是共识算法
上面巴拉巴拉一堆,已经说清楚了为什么要共识:各个Server间需要协调。
而共识算法,这就是这个协调过程的具体实现。
分布式共识算法最有名的Paxos共识算法,也是公认的很抽象的一种共识算法。而Raft不仅仅论文详细,还给出了2k多行的C++代码具体实现。目前也有很多用Raft作为共识算法的组件,例如etcd这些。
Raft共识算法介绍
Raft共识算法被在论文中被分解成三个重要组成部分。
- Leader Election(领导者选举)
- Log Replication(日志复制)
- Safety(安全性)
术语
- 节点状态:
- Leader:领导者节点,负责心跳,日志同步。
- Follower:随从节点。
- Candidate:候选人。
- Term:任期,类似于皇帝的朝代。当Term发生变化,称之为改朝换代。
- 心跳:特殊的AppendEntries RPCs
- 不带日志的RPC请求
Raft共识算法PartA:领导者选举与心跳
Leader Election(领导者选举)
Raft共识算法讲述的是一个强leader的算法。由leader来主导一切事务流程。Raft共识算法中提到,每一个Term中,整个集群中至多存在一个Leader。可以不存在Leader。
状态转换图
每种状态都有过期时间。
- Leader:自己挂掉,亦或者收到来自Term更高的心跳。
- Follower:心跳超时
- Candidate:选举超时
注意:除了Leader之外,另外两种状态会有多个节点持有,因此为了避免每次都同时过期,每个节点选择的过期时间是不一样的。论文中提到超时的时间选取可以在150ms - 300ms之间。
初始化,每个节点都是Follower状态,并且每个节点的心跳超时时间不同。当一个Follower发现心跳超时,那么这个Follower自动转换成为Candidate,选择一个选举过期时间,并且尝试投自己一票,然后并行的向其他节点发送求票消息。(注意这里是其他节点可能是Follower,也可能是Candidate)当Follower收到多数票的时候,自己变成Leader。
这里的投票状态有几个问题:
- (Vote Split)选票平分问题。如果选票被平分,那么这个Term里面就没有Leader。此时Candidate会超时,并且进行重新选举。
- 确定自己可不可以投票。只要发现求票者的任期比自己大,并且自己手中的票未投出,则可将票给予。注意,在一个Term中只能投一票。(这里还有一个限制:求票的Server日志必须比自己新,否则投反对票)
此时Leader已经被选出,Leader会定期发送心跳给Follower,宣誓自己的主权。(抑制选举)。
在集群正确运行一段时间后如果发生网络故障:
- Leader挂了,Follower超时,重新开始选举,重复上面的几步。当旧的Leader回来后,收到更大的Term心跳包,那么他会主动转换为Follower。
- Follower挂了,不影响。但是在重连回来的时候,收到了Term的心跳包,确认自己还是Follower,不会影响现任的Leader。
参考资料: