ceph源码分析--Monitor paxos算法

1.概述

对于分布式来说最重要的莫过于所有副本数据的一致性。

在monitor节点中,存在着Leader和Peon两种角色。当客户端发出读命令时可以由相应的Peon或者Leader返回。一旦发生修改动作,所有的消息会第一时间发送给Leader节点,然后由Leader节点分发给Peon节点。

paxos算法保证了一次修改操作只能批准一个值,从而保证了分布式系统副本的一致性。

2.paxos转换时机

1.monitor启动时,paxos初始化;
2.monitor进入bootstrap时,paxos的restart ;
3.monitor根据选举结果,paxos对应初始化为leader或peon;
4.monitor异常后,paxos recovery阶段;
5.monitor运行过程中,paxos决议;

1)monitor启动
Monitor::preinit()->Monitor::int_paxos()->Paxos::init()

初始化主要做了什么?

void Paxos::init()
{
  //load paxos variables from stable storage
  //上次当选leader时产生的PN,下次当选继续产生
  last_pn = get_store()->get(get_name(), "last_pn");
  //我接受的最后一个PN(来源未知)
  accepted_pn = get_store()->get(get_name(), "accepted_pn");
  //最后一次commit的paxos id
  last_committed = get_store()->get(get_name(), "last_committed");
  //日志中最早commit的paxos id(并不一定是第一个)
  first_committed = get_store()->get(get_name(), "first_committed");

  dout(10) << __func__ << " last_pn: " << last_pn << " accepted_pn: "
       << accepted_pn << " last_committed: " << last_committed
       << " first_committed: " << first_committed << dendl;

  dout(10) << "init" << dendl;
  assert(is_consistent());
}

接下来简单介绍下last_pn以及fist_committed:
last_pn会在发出paxos决议,以及选举成功paxos初始化为leader时更新,涉及函数Paxos::get_new_proposal_number。

version_t Paxos::get_new_proposal_number(version_t gt)
{
  if (last_pn < gt) 
    last_pn = gt;
  ...
  // 第n次选举后产生的pn值为100*n+rank(leader)
  last_pn /= 100;
  last_pn++;
  last_pn *= 100;
  last_pn += (version_t)mon->rank;
  ...
 }

之所以说fist_committed有可能不是第一个,是因为monitor为保证日志文件不至于太大,在每一次paxos执行完commit后会调用finish_round()->trim(),根据日志数量决定是否清理。

void Paxos::finish_round()
{
  dout(10) << __func__ << dendl;
  assert(mon->is_leader());
  // leader在commit完数据后调用该函数,设置状态为active,重新接受提案
  state = STATE_ACTIVE;
  ...
  //清理过量日志
  if (should_trim()) {
    trim();
  }
  if (is_active() && pending_proposal) {
  //表决下一个等待提案,并将状态置为updating
    propose_pending();
  }
}

first_committed更新时机如下:

bool should_trim() {
    int available_versions = get_version() - get_first_committed();
    int maximum_versions = g_conf->paxos_min + g_conf->paxos_trim_min;
    ////12.2.2版本对应的paxos_min500,paxos_trim_min为250
    if (trimming || (available_versions <= maximum_versions))
      return false;
    return true;
  }
void Paxos::trim()
{
  assert(should_trim());
  //12.2.2版本对应的paxos_min,paxos_trim_max为500
  version_t end = MIN(get_version() - g_conf->paxos_min,
              get_first_committed() + g_conf->paxos_trim_max);
...
  dout(10) << "trim to " << end << " (was " << first_committed << ")" << dendl;
  MonitorDBStore::TransactionRef t = get_pending_transaction();
...
}

可以看出日志条数会介于500-750之间,而first_commited则是保留下的最早的而非第一个。

2)paxos::restart()会在monitor的bootstrap时执行,主要是重置未处理的提案以及清理timeout的事件。

那么何时进入bootstrap()呢?
a.作为新加入的monitor节点加入集群
b.mon初始化
c.timer线程出现超时

void Paxos::restart()
{
..
  dout(10) << "restart -- canceling timeouts" << dendl;
  //清空所有的超时事件
  cancel_events();
  //清理提案的value
  new_value.clear();
  //如果正在写,先将写完成
  if (is_writing() || is_writing_previous()) {
    dout(10) << __func__ << " flushing" << dendl;
    mon->lock.Unlock();
    mon->store->flush();
    mon->lock.Lock();
    dout(10) << __func__ << " flushed" << dendl;
  }
  //设置状态为recovering
  state = STATE_RECOVERING;
  // 重置待决议的提案
  pending_proposal.reset();
...
}

3)选举完成时paxos初始化为peon或leader。
A.paxos初始化为peon

void Paxos::peon_init()
{
 ..
  cancel_events();
  new_value.clear();//与restart部分流程一致,不一一解释
  state = STATE_RECOVERING;
  lease_expire = utime_t();
  dout(10) << "peon_init -- i am a peon" << dendl;
  // 设置leader collect超时,超时重新选举
  reset_lease_timeout();
  // discard pending transaction
  pending_proposal.reset();
  ..
}

B.paxos初始化为leader

void Paxos::leader_init()
{
  cancel_events();
  new_value.clear();//与peon的初始化类似
  pending_proposal.reset();
  //单monitor节点集群直接将状态设置为active,等待propose提案
  if (mon->get_quorum().size() == 1) {
    state = STATE_ACTIVE;
    return;
  }
  //否则将状态设置为recovering,进入数据恢复状态
  state = STATE_RECOVERING;
  //发送collect消息,进行数据恢复,该过程会在情况4)中详细描述
  collect(0);
}

4)有的人会问,选举之前不是进行过数据同步吗?为什么选举结束,paxos初始化为leader时还需要进一步的数据恢复呢?
原因在于如果monitor之间的paxos版本号的差距在一定的范围内时不进行增量以及全同步,因此这里是非常必要的。

从leader的collect开始就进入了paxos的recover过程

void Paxos::collect(version_t oldpn)
{
  // we're recoverying, it seems!(重置状态)
  state = STATE_RECOVERING;
  assert(mon->is_leader());
  // reset the number of lasts received
  uncommitted_v = 0;
  uncommitted_pn = 0;
  uncommitted_value.clear();//清空之前提案的决议
  peer_first_committed.clear();
  peer_last_committed.clear();
  // look for uncommitted value
  if (get_store()->exists(get_name(), last_committed+1)) {
    version_t v = get_store()->get(get_name(), "pending_v");
    version_t pn = get_store()->get(get_name(), "pending_pn");
    if (v && pn && v == last_committed + 1) {
      uncommitted_pn = pn;
    } else {
      do
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值