博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Ceph Monitor源码机制分析(三)—— 选举
阅读量:4027 次
发布时间:2019-05-24

本文共 4700 字,大约阅读时间需要 15 分钟。

Monitor的选举机制

Monitor要做的事情很明确了,就是管理、维护和发布集群的状态信息,但是为了避免单点故障或者性能热点问题,一般使用多个Monitor来做这一件事情,也就是管理层有多个成员。集群的正常运行,首先需要管理层达成一致,达成一致就需要有一个能拍板的monitor(leader),大家都听它的就行了。所以要达成一致核心问题就是在众多monitor中选出那个能拍板的monitor。Ceph解决这个问题的方法很简单,有点类似于领导人的选举,即有资格的monitor先形成一个quorum(委员会),然后委员会的成员在quorum这个范围内选出一个leader,集群状态信息的更新以及quorum成员的维护就有这个leader负责。Leader的选取规则也比较简单,每个monitor在初始化的时候都会根据它的IP地址被赋予一个rank值,当选举leader时,rank值最小的monitor胜出当选leader。当quorum成员发生变化时(增加或者减少),都会触发重新选举流程,再选出一个leader。

整个Monitor的选举过程也是Monitor根据以下状态机进行状态变化的过程:

即Monitor在启动之后,会根据monmap发现其他的monitor并获取其他monitor的monmap、paxos版本等信息,然后酌情syncronize数据并触发quorum范围内的选举,选举之后monitor要么成为Leader要么成为Peon,直到服务停掉。

 

这么说还是有点笼统,让我们用代码说话吧,当然你得学会看代码。选举的入口函数是Monitor::start_election(),查一下调用这个函数的代码,不难看出会在以下三种情况发生时,调用它:

  • Monitor::handle_probe_reply(), monitor进行bootstrap时,首先会向monmap中所有的成员发送MMonProbe消息,然后在收到peer返回的probereply时,会根据返回的quorum信息以及paxos版本来判定是否需要发起选举。
  • Elector::handle_propose(),这个是在收到别的monitor发过来的选举请求消息时,会根据情况触发重新选举。
  • Monitor::do_admin_command()和Monitor::handle_command(),这两个属于通过ceph命令或者mon的admin socket执行quorumenter和quorum exit触发的选举操作。

这里详细介绍一下在第一种情况下触发的选举,并以此梳理一下整个选举过程。

  Monitor::bootstrap(),monitor进程的启动逻辑,具体做了以下几件事:

  • 开始时设置monitor的状态为STATA_PROBING
  • 然后判断是否设置了mon_compact_on_bootstrap参数,如果设置了,就执行compact操作,对monitor的store进行压缩
  • 如果集群只有一个monitor,则该monitor直接胜出
  • 根据mon_probe_timeout重置probe_timeout事件的时间
  • 如果monitor在monmap中,则将其将如到outside_quorum集合中。
  • 根据monmap,向其他peer一一发送MMonProbe消息。 

Monitor收到消息,经过dispatch逻辑之后进入Monitor::dispatch_op(),解析出来是MSG_MON_PROBE类型的消息,进而进入Monitor::handle_probe(),然后进入Monitor::handle_probe_reply进行处理。

  Monitor::handle_probe_reply(),在这里主要做了以下几件事。

  • 先判断当前monitor所处的状态如果是Probing或者Electing,则直接退出。
  • 比对对方的monmap和自己monmap的epoch版本,如果自己的monmap版本低,则更新自己的map,然后重新进入bootstrap()阶段。
  • 如果当前Monitor处于synchronizing阶段,则直接返回
  • 比对彼此的paxos的版本,如果对方的paxos版本较低,否则判断是否需要进行data的sync。这里有两种情况,如果自己的paxos版本是比对方的paxos_first_version纪录的版本低,则会进行sync操作。如果自己paxos的版本和对方的版本相差太远超过了设置的参数paxos_max_join_drift的值,也会先进行数据的sync而不会触发重新的选举操作
  • 如果从返回的消息中判断已经有一个quorum存在了,自己也在monmap中摒弃自己的ip地址不为空,则直接发起一个选举。否则,会请求加入这个quorum。
  • 如果没有现成的quorum,并且自己在monmap中,则把peer添加到outside_quorum的集合中。如果此时outside_quorum中的成员大于等于monmap->size() / 2 + 1时,开始选举,否则返回,等待条件满足。 

  Monitor::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()。 

  Monitor::start_election (),在这里主要做了以下几件事。

  • 从Mon store中读出mon的election_epoch存储在epoch中,更新epoch的值使其变为奇数,表明进入了选举cycle。epoch为偶数,表明已经形成了稳定的quorum。
  • 把自己加入到acked_me map中,并设置electing_me为true,希望大家选自己当leader。
  • 向monmap中的成员发送MMonElection::OP_PROPOSE消息。 

  其它的Monitor收到消息后,经过dispatch逻辑,即Monitor:: ms_dispatch() --> Monitor::_ms_dispatch() --> Monitor::dispatch_op()--> Elector::dispatch(),之后进入消息处理流程。

  • Elector::handle_propose(),首先确保收到消息的epoch版本是处于选举的版本(奇数)并且满足对feature的要求。接着判断将自己的选举epoch设置为和消息中包含的epoch的值。最后比对rank值,如果自己的rank值更小,则自己不ack此次选举,而是重新发起一轮选举。如果自己的rank值更大,则进入Elector::defer()流程,发送MMonElection::OP_ACK消息,ack该轮选举。

  发起选举的Monitor收到ACK消息之后,进入处理流程:

  • 将ACK自己的peer加入到acked_me这个map中,如果acked_me的个数和monmap中成员的个数一样,则表明选举成功,进入victory流程。这里有点需要搞清楚的是在有一个monitor down的情况下,剩余的monitor是如何选举成功的(acked_me的成员肯定和monmap的成员个数不相等)

  Leader会进入Elector::victory(),具体处理流程如下:

  • 将acked_me中的成员加入到quorum中,并且将election epoch的值加一使其变成偶数,标志选举过程结束。
  • 向quorum中的所有成员发送MMonElection::OP_VICTORY,消息通知大家选举结束。
  • 告诉monitor自己选举成功。 

  Leader进入Monitor::win_election(),具体处理流程如下:

  • 设置自己的状态为STATE_LEADER,清空outside_quorum中的成员。
  • 调用paxos->leader_init()初始化paxos,以及所有的paxos_service服务。在paxos的初始化中会设置paxos的状态为STATE_RECOVERING,并且调用Paxos::collect()函数,同步mon之间的数据,这个会在后面的Paxos数据更新部分介绍。
  • 启动health_monitor服务,目前主要是检查mon存储空间的使用情况。
  • 启动timecheck检查,确保monitor之间的时差不超过mon_clock_drift_allowed,如果超过就会报告mon clockskew。
  • 更新monitor的metadata,其主要纪录了以下信息:

[root@ceph02 ~]#ceph mon metadata ceph02{    "arch": "x86_64",    "cpu": "Intel Xeon E312xx(Sandy Bridge)",    "distro": "CentOS",    "distro_codename":"Core",    "distro_description":"CentOS Linux release 7.1.1503 (Core) ",    "distro_version":"7.1.1503",    "hostname": "ceph02",    "kernel_description": "#1SMP Tue Sep 15 15:05:51 UTC 2015",    "kernel_version":"3.10.0-229.14.1.el7.x86_64",    "mem_swap_kb": "0",    "mem_total_kb":"1884312",    "os": "Linux"}

  Peon在收到MMonElection::OP_VICTORY消息之后进入Elector::handle_victory(),具体处理流程如下:

  • 将自己的election epoch设置成消息中的epoch值。
  • 进入Monitor::lose_election(),设置自己的状态为STATE_PEON,调用peon_init初始化paxos以及相关的paxosservice,更新logger信息。
  • 取消自己的expire_event时间,即有参数mon_election_timeout控制的时间。 

至此,Monitor的选举过程就算结束了,但Paxos的状态还没有进入稳态,所以剩下的事情就是Leader来协调quorum中所有成员的数据同步了,这个主要是通过Paxos协议的两阶段提交机制来完成,整个过程相对比较复杂,会在后续数据更新机制中详细进行介绍。为了方便了解整个选举过程,我将主要的逻辑以时序图的形势展现出来,具体详见下图,图中主要以Leader为主线,给出了选举过程涉及的几个主要文件。

你可能感兴趣的文章
各种排序算法的分析及java实现
查看>>
SSH框架总结(框架分析+环境搭建+实例源码下载)
查看>>
js弹窗插件
查看>>
自定义 select 下拉框 多选插件
查看>>
js判断数组内是否有重复值
查看>>
js获取url链接携带的参数值
查看>>
gdb 调试core dump
查看>>
gdb debug tips
查看>>
arm linux 生成火焰图
查看>>
jtag dump内存数据
查看>>
linux和windows内存布局验证
查看>>
linux config
查看>>
linux insmod error -1 required key invalid
查看>>
linux kconfig配置
查看>>
linux不同模块completion通信
查看>>
linux printf获得时间戳
查看>>
C语言位扩展
查看>>
linux dump_backtrace
查看>>
linux irqdebug
查看>>
git 常用命令
查看>>