Ceph之Monitor分析3:选举流程

229 阅读4分钟

Monitor的选举流程:

Monitor在运行过程中,必须有一个Leader节点,所有写操作都是由Leader节点来完成,发送到Peon上的写操作也会转发到Leader节点上进行。

Leader的选择是根据rank值来定的,rank值小的为Leader,而rank值跟IP地址有关。

选举的时机:

1)收到quorum 推出exit或加入enter;2)收到选举消息;3)bootstrap()之后:单monitor节点情况下,直接赢得选举;多个monitors情况下,需要先probe其他monitor,probe的目的是同步monitor数据。

bootstrap()主要流程:

1、Probing和同步流程:Monitor1和其他Monitor为例说明

Monitor1

void Monitor::bootstrap()

{

#1 更新rank值

#2 Monitor进入STATE_PROBING状态:state = STATE_PROBING;

#3 _reset(); 重置quorum、paxos、paxos_service等服务

#4 如果就一个Monitor节点,不用选举,直接赢得选举:win_standalone_election();

#5 如果多个Monitor节点,那就有多个quorum成员,需要进行OP_PROBE探测:

#6 reset_probe_timeout();//重置probe超时时间

#7 outside_quorum.insert(name);//先把自己加入到outside_quorum中

#8 向monmap中其他monitor节点发送OP_PROBE消息。

send_mon_message(new MMonProbe(monmap->fsid, MMonProbe::OP_PROBE, name, has_ever_joined, ceph_release()), i);

}

其他Monitor

收到OP_PROBE消息,处理:

void Monitor::handle_probe_probe(MonOpRequestRef op)

{

#1 校验特性,若missing feature,则发送OP_MISSING_FEATURES

#2 若自己的版本旧,则重新bootstrap():if (paxos->get_version() + 1 < m->paxos_first_version)

#3 否则,回复REPLY消息:(first_committed、last_committed)

MMonProbe(monmap->fsid, MMonProbe::OP_REPLY, name, has_ever_joined, ceph_release());

#4 若发送方不在monmap中,把它加入extra_probe_peers;若发送方在monmap中,把它加入live_pinging,做ping check。

}

Monitor1

收到OP_REPLY消息,处理:

void Monitor::handle_probe_reply(MonOpRequestRef op)

{

#1 不是probing,也不是electing,则退出。

#2 若自己monmap的epoch比较低,则更新monmap,重新bootstrap()

#3 若自己的paxos版本paxos_first_version、paxos_last_version低,则全同步sync_start

if (paxos->get_version() < m->paxos_first_version && m->paxos_first_version > 1),则sync_start(other, true);

if (paxos->get_version() + g_conf()->paxos_max_join_drift < m->paxos_last_version),则sync_start(other, false);

void Monitor::sync_start(entity_addrvec_t &addrs, bool full)

{

#3.1 state = STATE_SYNCHRONIZING; Monitor进入STATE_SYNCHRONIZING状态。

#3.2 根据是否全同步,发送同步消息:OP_GET_COOKIE_FULL或OP_GET_COOKIE_RECENT

MMonSync *m = new MMonSync(sync_full ? MMonSync::OP_GET_COOKIE_FULL : MMonSync::OP_GET_COOKIE_RECENT);

#3.3 收到OP_COOKIE消息,处理:

void Monitor::handle_sync_cookie(MonOpRequestRef op)

{

sync_get_next_chunk();发送OP_GET_CHUNK消息

MMonSync *r = new MMonSync(MMonSync::OP_GET_CHUNK, sync_cookie);

}

#3.4 收到OP_CHUNK/OP_LAST_CHUNK消息,处理:

void Monitor::handle_sync_chunk(MonOpRequestRef op)

{

若是OP_CHUNK,继续同步sync_get_next_chunk(),发送OP_GET_CHUNK消息

若是OP_LAST_CHUNK,同步完成:sync_finish(m->last_committed);

}

}

#4 从消息中判断已经存在quorum:if (m->quorum.size())

#4.1 若自己也在monmap,则直接发起选举start_election()

#4.2 若自己不在monmap中,或地址为空,发送消息申请加入send_mon_message(new MMonJoin

#5 若不存在quorum,把发消息的节点加入到outside_quorum中;一旦outside_quorum中成员大于最小quorum的值monmap->min_quorum_size(),即:超过一半mon_info.size()/2 +1,则开始选举;否则,等待其他Monitor的Reply消息回复。

}

其他Monitor

收到OP_GET_COOKIE_FULL和OP_GET_COOKIE_RECENT消息,处理:

void Monitor::handle_sync_get_cookie(MonOpRequestRef op)

{

回复OP_COOKIE消息:

MMonSync *reply = new MMonSync(MMonSync::OP_COOKIE, sp.cookie);

}

收到OP_GET_CHUNK消息,处理:

void Monitor::handle_sync_get_chunk(MonOpRequestRef op)

{

发送OP_CHUNK消息:

MMonSync *reply = new MMonSync(MMonSync::OP_CHUNK, sp.cookie);

发送OP_LAST_CHUNK消息:

reply->op = MMonSync::OP_LAST_CHUNK;

}

2、Leader选举流程主要步骤:

Monitor1

void Monitor::start_election()

{

#1 wait_for_paxos_write();//等待paxos的状态更新完成

#2 _reset();//重置monitor服务:重启paxos和paxosService

#3 state = STATE_ELECTING;//设置Monitor的状态为STATE_ELECTING

#4 elector.call_election();//开始选举,进入logic.start();

void ElectionLogic::start()

{

#4.1 acked_me.clear();//清空acked_me,表示没有人选自己。

#4.2 init();//更新epoch的值,奇数表示进入选举。

#4.3 epoch为偶数(epoch % 2 == 0),则epoch+1,进入选举流程:bump_epoch(epoch+1); // odd == election cycle

#4.4 将本节点monitor1加入acked_me中:acked_me.insert(elector->get_my_rank()),同时设置electing_me = true,希望大家都选自己monitor1当Leader。

#4.5 向monmap中各个成员,发送OP_PROPOSE消息:

MMonElection *m = new MMonElection(MMonElection::OP_PROPOSE

mon->send_mon_message(m, i);

#4.6 重置定时器:elector->_start();

}

}

其他Monitor

收到OP_PROPOSE消息,处理:判断决定支持发送方还是自己

void Elector::handle_propose(MonOpRequestRef op)

{

#1 确保收到的epoch是奇数(在选举状态),且满足各种feature;

logic.receive_propose(from, m->epoch, oct); -> propose_classic_handler(from, mepoch);//根据策略,选择处理: //epoch大且rank小的,才能当选Leader。

#2 若对方epoch比自己大(mepoch > epoch),放弃选自己,追随对方:bump_epoch(mepoch);

#3 若我的epoch大(mepoch < epoch),不追随对方,发起选举start_election()

#4 若我比对方的rank值小:若我响应过其他比我rank值小的,那么不再回复,意味着本节点不是rank值最小的节点;否则若我没有响应过其他比我rank值小的,发起选择start_election(),表示我比对方更能当选Leader。

#5 若发送方rank小,我的rank值大,且之前没有赞成过谁,或赞成的rank没现在的小,执行defer(),支持发送方成为Leader。

void ElectionLogic::defer(int who)

{

#5.1 放弃选择自己为Leader:acked_me.clear(); electing_me = false;

#5.2 发送OP_ACK消息给对方:elector->_defer_to(who);

MMonElection *m = new MMonElection(MMonElection::OP_ACK, get_epoch(),

}

}

Monitor1

收到OP_ACK消息,处理:

void Elector::handle_ack(MonOpRequestRef op)

{

void ElectionLogic::receive_ack(int from, epoch_t from_epoch)

{

#1 确保收到的epoch是奇数(在选举状态)

#2 若自己因为某些原因重启了,epoch会比发送方的epoch小(from_epoch > epoch),更新epoch,再重新开始自荐选举start()

#3 如果我正在推选自己if (electing_me) ,且所有人都赞成我成为Leader,则选举胜利:declare_victory();

#4 否则说明以前推举过自己,但现在已投别人了,即:已经做过defer()了,支持别人成为Leader。

}

void ElectionLogic::declare_victory()

{

#1 把回复过我的节点acked_me,加入到新的quorum中:new_quorum.swap(acked_me);

#2 更新epoch为偶数:bump_epoch(epoch+1); // is over!

#3 发OP_VICTORY消息给所有quorum成员,通知大家本节点当选Leader。

MMonElection *m = new MMonElection(MMonElection::OP_VICTORY, get_epoch(),

#4 告诉Monitor赢得选举,标记自己为Leader:mon->win_election(get_epoch(), quorum, cluster_features, mon_features, min_mon_release, metadata);

void Monitor::win_election(epoch_t epoch, const set& active, uint64_t features,

const mon_feature_t& mon_features, ceph_release_t min_mon_release, const map<int,Metadata>& metadata)

{

#1 设置本Monitor节点状态为STATE_LEADER:state = STATE_LEADER;

#2 清空outside_quorum中的成员:outside_quorum.clear();

#3 设置leader支持的命令:set_leader_commands(get_local_commands(mon_features));

#4 paxos->leader_init();//leader角色初始化:设置Paxos状态为STATE_RECOVERING:state = STATE_RECOVERING;,Leader进入phase 1:执行collect()同步monitor数据。

#5 本次选举结束:monmon()->election_finished();

PaxosService::_active() -> MonmapMonitor::on_active()

t->put(Monitor::MONITOR_NAME, "joined", 1);

mon.store->apply_transaction(t);

mon.has_ever_joined = true;//正式加入quorum集群

#6 更新monitor的metadata,将metadata存入DB中:t->put(MONITOR_STORE_PREFIX, "last_metadata", bl);

#7 完成选举:finish_election();

}

}

}

其他Monitor

收到OP_VICTORY消息,处理:

void Elector::handle_victory(MonOpRequestRef op)

{

bool ElectionLogic::receive_victory_claim(int from, epoch_t from_epoch)

{

#1 若此时epoch不匹配(from_epoch != epoch + 1),说明重启了,推荐自己,重新选举start()

#2 接受对方选择胜利,自己mon->lose_election();

void Monitor::lose_election(epoch_t epoch, set &q, int l, uint64_t features, const mon_feature_t& mon_features, ceph_release_t min_mon_release)

{

#2.1 设置本Monitor节点状态为STATE_PEON:state = STATE_PEON;

#2.2 清空outside_quorum中的成员:outside_quorum.clear();

#2.3 Peon初始化:paxos->peon_init();,设置Paxos状态为STATE_RECOVERING:state = STATE_RECOVERING;

#2.4 完成选举:finish_election();

}

#3 mon->set_leader_commands(new_cmds);//存放leader的命令

}

}