集群选举算法实现
⼀个分布式服务集群管理通常需要⼀个协调服务,提供服务注册、服务发现、配置管理、组服务等功能,⽽协调服务⾃⾝应是⼀个⾼可⽤的
服务集群,ZooKeeper是⼴泛应⽤且众所周知的协调服务。协调服务⾃⾝的⾼可⽤需要选举算法来⽀撑,本⽂将讲述选举原理并以分布式
服务集群的协调服务为例详细说明协调服务的选举实现。
为什么要选NebulaBeacon来说明协调服务的选举实现?⼀⽅⾯是我没有读过Zookeeper的代码,更重要的另⼀⽅⾯是NebulaBeacon
的选举实现只有两百多⾏代码,简单精炼,很容易讲清楚。基于⾼性能C++⽹络框架实现的分布式服务集群是⼀种⽤C++快速构建⾼性能分
布式服务的解决⽅案。
为什么要实现⾃⼰的协调服务⽽不直接⽤Zookeeper?想造个C++的轮⼦,整个集群都是C++服务,因为选了ZooKeeper⽽需要部署
⼀套Java环境,配置也跟其他服务不是⼀个体系,实在不是⼀个好的选择。Spring Cloud有Eureka,NebulaBootstrap有
NebulaBeacon,未来NebulaBootstrap会⽀持ZooKeeper,不过暂⽆时间表,还是⾸推NebulaBeacon。
1. 选举算法选择
Paxos算法 和 ZooKeeper ZAB协议 是两种较⼴为⼈知的选举算法。ZAB协议主要⽤于构建⼀个⾼可⽤的分布式数据主备系统,例如ZooKeeper,⽽Paxos算法则是⽤于构建⼀个分布式的⼀致性状态机系统。也有很多应⽤程序采⽤⾃⼰设计的简单的选举算法,这类电脑网卡怎么办
型简
单的选举算法通常依赖计算机⾃⾝因素作为选举因⼦,⽐如IP地址、CPU核数、内存⼤⼩、⾃定义序列号等。
Paxos规定了四种⾓⾊洗羽绒服怎么洗
(Propor,Acceptor,Learner,以及Client)和两个阶段(Promi和Accept)。 ZAB服务具有四
种状态:LOOKING、FOLLOWING、LEADING、OBSERVING。 NebulaBeacon是⾼可⽤分布式系统的协调服务,采⽤ZAP协议更
为合适,不过ZAP协议还是稍显复杂了,NebulaBeacon的选举算法实现基于节点的IP地址标识,选举速度快,实现⼗分简单。
2. 选举相关数据结构
NebulaBeacon的选举相关数据结构⾮常简单:
const uint32 SessionOnlineNodes::mc_uiLeader = 0x80000000; ///< uint32最⾼位为1表⽰leader
const uint32 SessionOnlineNodes::mc_uiAlive = 0x00000007; ///< 最近三次⼼跳任意⼀次成功则认为在线 std::map<std::string, uint32> m_mapBeacon; ///< Key为节
如上数据结构m_mapBeacon保存了Beacon集群各Beacon节点信息,以Beacon节点的IP地址标识为key排序,每次遍历均从头开
始,满⾜条件(1&&2 或者 1&&3)则标识为Leader:1. 节点在线;2. 已经成为Leader; 3. 整个列表中不存在在线的Leader,⽽节点
处于在线节点列表的⾸位。
3. Beacon选举流程
Beacon选举基于节点IP地址标识,实现⾮常简单且⾼效。
"beacon":["192.168.1.11:16000", "192.168.1.12:16000"]
进程启动时⾸先检查,若未配置其他Beacon节点信息,则默认只有⼀个Beacon节点,此时该节点在启动时⾃动成为Leader节点。否
则,向其他Beacon节点发送⼀个⼼跳消息,等待定时器回调检查并选举出Leader节点。选举流程如下图:
检查是否在线就是通过检查两次定时器回调之间是否收到了其他Beacon节点的⼼跳消息。对m_mapBeacon的遍历检查判断节点在线情况,对已离线的Leader节点置为离线状态,若当前节点应成为Leader节点则成为Leader节点。
4. Beacon节点间选举通信
Beacon节点间的选举通信与节点⼼跳合为⼀体,这样做的好处是当leader节点不可⽤时,fllower节点⽴刻可以成为leader节点,选举过程只需每个fllower节点遍历⾃⼰内存中各Beacon节点的⼼跳信息即可,⽆须在发现leader不在线才发起选举,更快和更好地富人理财
保障集群的⾼可⽤性。
Beacon节点⼼跳信息带上了leader节点作为协调服务产⽣的新数据,fllower节点在接收⼼跳的同时完成了数据同步,保障任意⼀个fllower成为leader时已获得集群所有需协调的信息并可随时切换为leader。除定时器触发的⼼跳带上协调服务产⽣的新数据之外,leader 节点产⽣新数据的同时会⽴刻
向fllower发送⼼跳。
5. Beacon选举实现
Beacon⼼跳协议proto:
/**
* @brief BEACON节点间⼼跳
*/
message Election
{
int32 is_leader = 1; ///< 是否主节点
uint32 last_node_id = 2; ///< 上⼀个⽣成的节点ID repeated uint32 added_node_id = 3; ///< 新增已使⽤的节点ID repeated uint32 removed_node_id = 检查Beacon配置,若只有⼀个Beacon节点则⾃动成为Leader:
void SessionOnlineNodes::InitElection(const neb::CJsonObject& oBeacon)
{
neb::CJsonObject oBeaconList = oBeacon;
for (int i = 0; i < oBeaconList.GetArraySize(); ++i) { m_mapBeacon.inrt(std::make_pair(oBeaconList(i) + ".1", 0)); } if (m_mapBeacon.size() == 0) { m_bIsLead 发送Beacon⼼跳:
void SessionOnlineNodes::SendBeaconBeat()
{
LOG4_TRACE("");
MsgBody oMsgBody;
Election oElection;
if (m_bIsLeader)
{
oElection.t_is_leader(1);
oElection.t_last_node_id(m_unLastNodeId);
for (auto it = m_tAddedNodeId.begin(); it != d(); ++it) { oElection.add_added_node_id(*it); } for (auto it = m_tRemovedNodeId.beg 番茄早疫病
接收Beacon⼼跳:
void SessionOnlineNodes::AddBeaconBeat(const std::string& strNodeIdentify, const Election& oElection) { 军训歌
if (!m_bIsLeader) { if (oElection.last_node_id() >
检查在线leader,成为leader:
void SessionOnlineNodes::CheckLeader()
{
LOG4_TRACE("");
std::string strLeader;
for (auto iter = m_mapBeacon.begin(); iter != d(); ++iter) { if (mc_uiAlive & iter->cond) { if (mc_uiLeader & iter->cond) { strLeader = iter->
6. Beacon节点切换leader
通过Nebula集群的命令⾏管理⼯具可以很⽅便的查看Beacon节点状态,nebcli的使⽤说明见Nebcli项⽬的README。下⾯启动三个
Beacon节点,并反复kill掉Beacon进程和重启,查看leader节点的切换情况。
启动三个beacon节点:
nebcli): show beacon
node is_leader is_online 192.168.157.176:16000.1 yes yes 192.168.157.176:17000.1 no yes 192.168.157.176:18000.1 no yes
kill掉leader节点:
nebcli): show beacon
node is_leader is_online 192.168.157.176:16000.1 no no 192.168.157.176:17000.1 yes yes 192.168.157.176:18000.1 no yes
kill掉fllower节点:
nebcli): show beacon
node is_leader is_online 192.168.157.176:16000.1 no no 192.168.157.176:17000.1 yes yes 192.168.157.176:18000.1 no no
重启被kill掉的两个节点:
nebcli): show beacon
node is_leader is_online 192.168.157.176:16000.1 no yes 192.168.157.176:17000.1 ye黑灾
s yes 192.168.157.176:18000.1 no yes
fllower节点在原leader节点不可⽤后成为leader节点,且只要不宕机则⼀直会是leader节点,即使原leader节点重新变为可⽤状态也不会再次切换。
7. 结束
开发Nebula框架⽬的是致⼒于提供⼀种基于C++快速构建⾼性能的分布式服务。如果觉得本⽂对你有⽤,别忘了到Nebula的或给个star,谢谢。