Ceph Monitor选主(上)

monitor选主主要包括前期准备和选主两部分

选主前期准备

发送探测消息
任何一个monitor节点都可以发起选举,选举的入口函数是bootstrap

bootstrap
	cancel_probe_timeout();
		if (probe_timeout_event)
			timer.cancel_event(probe_timeout_event);
			probe_timeout_event = NULL;
	// monitor状态改变
	state = STATE_PROBING;	
	_reset(); 
	for (unsigned i = 0; i < monmap->size(); i++)
		if ((int)i != rank)
			messenger->send_message(new MMonProbe(monmap->fsid, MMonProbe::OP_PROBE, name, has_ever_joined), monmap->get_inst(i));	
	for (set<entity_addr_t>::iterator p = extra_probe_peers.begin(); p != extra_probe_peers.end();
		if (*p != messenger->get_myaddr())
			messenger->send_message(new MMonProbe(monmap->fsid, MMonProbe::OP_PROBE, name, has_ever_joined), i);

bootstrap会向monmap和extra_probe_peers中的节点发送OP_PROBE消息,其中extra_probe_peers表示那些用户认为已经添加成功,实际上集群中所有的节点并不全都获悉该节点的加入。比如集群有节点A(主)、B,用户向A发送命令添加C,此时A向B同步最新monmap的时候故障,则B并不知道C的加入,C节点启动后,会向monmap中的B节点发送OP_PROBE消息,B节点发现C节点不在自己的monmap中,所以将其加入extra_probe_peers。

回应探测消息

MMonProbe *r;
r = new MMonProbe(monmap->fsid, MMonProbe::OP_REPLY, name, has_ever_joined);
r->name = name;
r->quorum = quorum;
monmap->encode(r->monmap_bl, m->get_connection()->get_features());
r->paxos_first_version = paxos->get_first_committed();
r->paxos_last_version = paxos->get_version();
m->get_connection()->send_message(r);

处理回应

monmap->encode(mybl, m->get_connection()->get_features());
// 对方的monmap和自己的不相同
if (!mybl.contents_equal(m->monmap_bl))
	newmap->decode(m->monmap_bl);
	/*
		满足以下条件,自己就会更新monmap,并重新bootstrap
		(1) 对方参与过一轮完整的选举。
		(2) 自己没有参与过一轮完整的选举。
		(3) 对方的monmap版本比自己更新。
		这常常发生在一个新的monitor节点加入时。
	*/
	if (m->has_ever_joined && (newmap->get_epoch() > monmap->get_epoch() !has_ever_joined))
		monmap->decode(m->monmap_bl);
		bootstrap();
		return;
// 如果当前monitor不处于STATE_PROBING阶段,就忽略这个ACK。比如在真正选举的时候,有ACK到达,就忽略这个消息,这也说明可能会忽略具有更新日志节点的ACK,导致本节点的日志不是最新的。因此,在真正选举阶段仍然会对比自己和monmap中其它节点的日志版本,如果有必要会再次进行恢复。
if (!is_probing()) 
	return;
// 如果当前monitor处于STATE_SYNCHRONIZING阶段,也忽略这个消息。因此,monitor一次只能恢复根据一个其它节点恢复日志。
if (is_synchronizing())
	return;
// sync_last_committed_floor是上次同步的开始版本,如果sync_last_committed_floor大于对方的最新版本,就不需要同步了
if (m->paxos_last_version >= sync_last_committed_floor)
	// 自己的paxos最新确认版本落后于对方记录的最老确认版本,需要全量更新,这一般在一个新monitor加入时会出现
	if (paxos->get_version() < m->paxos_first_version && m->paxos_first_version > 1)
		cancel_probe_timeout();
		sync_start(other, true);
			if (sync_full)
				auto t(std::make_shared<MonitorDBStore::Transaction>());
				t->put("mon_sync", "in_sync", 1);
				sync_last_committed_floor = std::max(sync_last_committed_floor, paxos->get_version());
				t->put("mon_sync", "last_committed_floor", sync_last_committed_floor);
			MMonSync *m = new MMonSync(sync_full ? MMonSync::OP_GET_COOKIE_FULL : MMonSync::OP_GET_COOKIE_RECENT);
			// 如果是增量更新,就记录本节点的最新确认版本
			if (!sync_full)
				m->last_committed = paxos->get_version();
			messenger->send_message(m, sync_provider);
		return;
	// 自己的最新确认版本落后对方最新确认版本部分,增量更新
	if (paxos->get_version() + g_conf->paxos_max_join_drift < m->paxos_last_version) {
		cancel_probe_timeout();
		sync_start(other, false);
		return;
/*
	到这里就说明不需要同步数据,如果对方的quorum不为空,则说明对方处于选举结束的时间段,
	要不是你要求选举,我才不会理你。
*/
// Monitor::_reset会清quorum
if (m->quorum.size())
	// 如果monmap包含本节点,则开启选举,这一般发生在一个节点下线后立马又上线,或者新加入的节点同步完成。
	if (monmap->contains(name) && !monmap->get_addr(name).is_blank_ip())
		start_election();
	else
		// 发生在用户不实现调用mon add命令添加一个节点,就启动一个节点?
		messenger->send_message(new MMonJoin(monmap->fsid, name, messenger->get_myaddr()), monmap->get_inst(*m->quorum.begin()));
else
	// 比如全部节点都重启,则会进入这一步
	if (monmap->contains(m->name))
		outside_quorum.insert(m->name);
	else
		return;
	// 要想重新选举,得确定能和过半的节点通信
	unsigned need = monmap->size() / 2 + 1;
	if (outside_quorum.size() >= need)	
		if (outside_quorum.count(name))
			start_election();

可以看到PROBE的目的就是为了探测自己的信息是否和集群中的其它节点有偏差,如果有就需要更新。

你可能感兴趣的:(ceph)