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的目的就是爲了探測自己的信息是否和集羣中的其它節點有偏差,如果有就需要更新。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章