ceph存储的monitor选举流程
1. Monitor::init函数:(启动函数)
启动定时器,准备好之后 调用触发函数bootstrap。每个monitor在初始化的时候都会根据它的IP地址被赋予一个rank值,当选举leader时,rank值最小的monitor胜出当选leader。
rank值代表mon在monmap中的位置,-1代表不在monmap中。
2. bootstrap(触发函数)
Monitor::bootstrap(),monitor进程的启动逻辑,具体做了以下几件事:
1.开始时设置monitor的状态为STATA_PROBING
2.然后判断是否设置了mon_compact_on_bootstrap参数,如果设置了,就执行compact操作,对monitor的store进行压缩
3.如果集群只有一个monitor,则该monitor直接胜出
4.根据mon_probe_timeout重置probe_timeout事件的时间
5.如果monitor在monmap中,则将其将如到outside_quorum集合中。
6.根据monmap,向其他peer一一发送MMonProbe消息。
- 调用wait_for_paxos_write,判断paxos的状态,如果poaxs正在写或等待写,则释放mon锁,等待写完成了载加锁。(结束之前io)
- 调用get_rank函数从monmap中得到自己的rank值。如果得到的新的rank值与旧的rank值不同,则通知所有的peer(同等级的mon)。给所有节点发送MMonProbe::OP_PROBE
- 触发选举后发送MMonProbe::OP_PROBE消息,
3. handle_probe(消息处理函数) - 接收所有消息
- 调用Monitor::handle_probe_probe和Monitor::handle_probe_reply、还有handle_probe_stop、handle_probe_assert_stop.
4. Monitor::handle_probe_probe - 1.如果missing feature,发送OP_MISSING_FEATURES的信息,结束。
- 若message的发送方的版本比自己新,无法通过paxos算法部分做数据修复,需要重新bootstrap()从对方主动拉数据。(只要版本有更新,就要重新进入bootstarp函数)。
- 正常流程,汇报paxos状态,last_commit,first_commit信息,发送OP_REPLY消息。
- 如果发现了一个peer,那么extra_probe_peers.insert(m->get_source_addr())
5. Monitor::handle_probe_reply()
Monitor::handle_probe_reply(),在这里主要做了以下几件事。
1.先判断当前monitor所处的状态如果是Probing或者Electing,则直接退出。
2.比对对方的monmap和自己monmap的epoch版本,如果自己的monmap版本低,则更新自己的map,然后重新进入bootstrap()阶段。
3.如果当前Monitor处于synchronizing阶段,则直接返回
4.比对彼此的paxos的版本,如果对方的paxos版本较低,否则判断是否需要进行data的sync。这里有两种情况,如果自己的paxos版本是比对方的paxos_first_version纪录的版本低,则会进行sync操作。如果自己paxos的版本和对方的版本相差太远超过了设置的参数paxos_max_join_drift的值,也会先进行数据的sync而不会触发重新的选举操作(sync_start(other,false),false代表不选举)。
5.如果从返回的消息中判断已经有一个quorum存在了,自己也在monmap中摒弃自己的ip地址不为空,则直接发起一个选举。否则,会请求加入这个quorum。
6.如果没有现成的quorum,并且自己在monmap中,则把peer添加到outside_quorum的集合中。如果此时outside_quorum中的成员大于等于monmap->size() / 2 + 1时,开始选举,否则返回,等待条件满足。
1. 比较epoch消息和paxos和自己的是否一样,保持每个mon一致
2. 之后if(m->quorum.size);当quorum.size不为0,则发起选举(调用start_election);
3. 当quorum.size为0时,当发现自己处于monmap中,则把peer加入到outside_quorum中,当out_quorum.size>=need时(need=monmap/2+1),调用start_election开始选举。
6. 开始选举start_election函数
- 如果Paxos正在STATE_WRITING或者STATE_WRITING_PREVIOUS状态,则等待paxos的更新完成。
- 重置monitor中的服务,包括probe timeout事件、停止时间检查(mon time skew的检查)、health检查事件、scrub事件等,并且restart paxos以及所有的paxos service服务。
- 设置自己进入STATE_ELECTING状态,并增加l_mon_num_elections和l_mon_election_call这些统计数据。
- 调用elector的call_election()。
7. 开始选举 - start_election函数中elector.call_election()调用start()
从Mon->store中读出mon的election_epoch存储在epoch中,初始值为1,更新epoch的值使其变为奇数,表明进入了选举cycle。epoch为偶数,表明已经形成了稳定的quorum。
把自己加入到acked_me 集合中,并设置electing_me为true,希望大家选自己当leader。
向monmap中的成员发送MMonElection::OP_PROPOSE消息。
其它的Monitor收到消息后,经过dispatch逻辑,即Monitor:: ms_dispatch() --> Monitor::_ms_dispatch() --> Monitor::dispatch_op()--> Elector::dispatch(),之后进入消息处理流程。
8. Elector::handle_propose(),首先确保收到消息的epoch版本是处于选举的版本(奇数)并且满足对feature的要求。
接着判断将自己的选举epoch设置为和消息中包含的epoch的值。
最后比对rank值,(主mon走)如果自己的rank值更小,且选举是自己发起的,则自己不ack此次选举;若不是自己发起的则是重新发起一轮选举。(选举必须是rank值最小的mon发起的)
(备mon走)如果自己的rank值更大证明已经选出主,则备mon会进入Elector::defer()流程,发送MMonElection::OP_ACK消息,ack该轮选举。
9. 发起选举的Monitor收到ACK消息之后Elector::handle_ack(),进入处理流程:
将ACK自己的peer加入到acked_me这个map中,如果acked_me的个数和monmap中成员的个数一样,则表明选举成功,进入victory流程。
这里有点需要搞清楚的是在有一个monitor down的情况下,剩余的monitor是如何选举成功的(acked_me的成员肯定和monmap的成员个数不相等)。
10. Leader会进入Elector::victory()(主mon走),选举成功函数,具体处理流程如下:
将acked_me中的成员加入到quorum中,并且将election epoch的值加一使其变成偶数,标志选举过程结束。
向quorum中的所有成员发送MMonElection::OP_VICTORY,消息通知大家选举结束。
告诉monitor自己选举成功。
11. Leader进入Monitor::win_election(),具体处理流程如下:
设置自己的状态为STATE_LEADER,清空outside_quorum中的成员。
调用paxos->leader_init()初始化paxos,以及所有的paxos_service服务。在paxos的初始化中会设置paxos的状态为STATE_RECOVERING,并且调用Paxos::collect()函数,同步mon之间的数据,这个会在后面的Paxos数据更新部分介绍。
(paxos->leader_init()设置状态STATE_RECOVERING重构、、Paxos::collect()函数,同步mon之间的数据)
启动health_monitor服务,目前主要是检查mon存储空间的使用情况。
启动timecheck检查,确保monitor之间的时差不超过mon_clock_drift_allowed,如果超过就会报告mon clockskew。
更新monitor的metadata,其主要纪录了以下信息:
12. Peon在收到MMonElection::OP_VICTORY消息之后进入Elector::handle_victory()(备mon走),具体处理流程如下:
将自己的election epoch设置成消息中的epoch值。
进入Monitor::lose_election(),设置自己的状态为STATE_PEON,调用peon_init初始化paxos以及相关的paxosservice,更新logger信息。
取消自己的expire_event时间,即有参数mon_election_timeout控制的时间。
mon的启动流程
- 初始化
全部初始化,包括全局变量、日志模块启动、日志线程 - 启动线程
启动service线程(负责重新打开日志文件)
启动admin socke(获取配置,修改配置) - 构造对象
构造mon db对象、构造Messenger绑定网卡、构造Monitor对象 - 启动
1.Monitor预初始化(初始化paxos服务,注册adminsocket命令)
2.启动messenger
3.monitor初始化:启动定时器,添加定时器事件,触发选举
4.注册信号处理函数
mon选举流程 - 介绍monitor的一致性服务
确保Monitor一致性服务的是Multi-Paxos,ceph中用Paxos来代替Multi-Paxos。
Paxos节点与monitor节点绑定,每个mon启动一个Paxos,Paxos为mon提供服务。其中一个Paxos节点作为leader其余的为peon角色。Lerder可以发起议案,peon根据自己的本地历史选择接受或拒绝议案,并回复leader,leder提交超过半数Paxos节点接收的议案,这些Paxos节点被称为quorum(法定人数)。 - Paxos节点的其他状态:
- probing状态(发现并更新节点信息)
- electing状态(用来选出leader,probing状态受到过半数的认可后变为electing状态)
- synchronizing状态(probing发现数据差异过大时会进入synchronizing状态进行同步)
- mon的选举过程
paxos算法(少数服从多数) - 每个节点可以作为提议者和接收者,提议者给超过半数的接收者发送预提案。(预提案中有一个提案编号n)。
- 接收者接收到n后和之前接收到的其他预提案比较,如果n大于之前的提案编号,就反馈“接受”,(并且这个接收者不会再接受编号小于n的提案),并把自己之前接受过的编号最大的提案一并反馈回去。如果n小之前接受到的提案编号,就反馈“拒绝”,并一并反馈之前接受到的最大的提案编号。
- 如果提议者收到了大多数接收者的“接受”信息后,那么这个提议者的提议就被认可,它将发送一个正式提案给所有节点,包括编号n和值v(v就是leader的编号,是提议者接收到反馈中编号最大的提案的v值,n是预提案编号),等待接收者接受。
- 接收者收到正式提案,接受这个提议,除非在这个过程中他接受到了更大的提案编号,并反馈了接受。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构