ceph mds启动流程

本文的所有的分析仅基于个人理解,代码基于ceph nautilus版本

MDSDaemon Start

首先一切的开始自然是mds进程的上电过程:

src/ceph_mds.cc

int main(int argc, const char **argv)
{

  // 全局初始化,任何类型ceph daemon启动时都需要调用global_init来完成一些初始化工作
  auto cct = global_init(NULL, args,
             CEPH_ENTITY_TYPE_MDS, CODE_ENVIRONMENT_DAEMON,
             0, "mds_data");
  // 初始化堆栈分析器,如果设置了CEPH_HEAP_PROFILER_INIT的环境变量后才会启动,且需要google-perftools才能使用
  // 默认情况下是不启动的,后续可以通过ceph tell mds.$name heap start_profiler来启动
  ceph_heap_profiler_init();


  // 对fork的封装
  Preforker forker;

  entity_addrvec_t addrs;
  pick_addresses(g_ceph_context, CEPH_PICK_ADDRESS_PUBLIC, &addrs);

  // Normal startup
  if (g_conf()->name.has_default_id()) {
    derr << "must specify '-i name' with the ceph-mds instance name" << dendl;
    exit(1);
  }

  if (g_conf()->name.get_id().empty() ||
      (g_conf()->name.get_id()[0] >= '0' && g_conf()->name.get_id()[0] <= '9')) {
    derr << "MDS id '" << g_conf()->name << "' is invalid. "
      "MDS names may not start with a numeric digit." << dendl;
    exit(1);
  }

  // fork子进程并等待子进程退出??
  if (global_init_prefork(g_ceph_context) >= 0) {
    std::string err;
    int r = forker.prefork(err);
    if (r < 0) {
      cerr << err << std::endl;
      return r;
    }
    if (forker.is_parent()) {
      if (forker.parent_wait(err) != 0) {
        return -ENXIO;
      }
      return 0;
    }
    // 为子进程执行global_init
    global_init_postfork_start(g_ceph_context);
  }
  common_init_finish(g_ceph_context);
  global_init_chdir(g_ceph_context);

  auto nonce = ceph::util::generate_random_number<uint64_t>();

  // 创建一个Messenger实例并进行相关配置
  std::string public_msgr_type = g_conf()->ms_public_type.empty() ? g_conf().get_val<std::string>("ms_type") : g_conf()->ms_public_type;
  Messenger *msgr = Messenger::create(g_ceph_context, public_msgr_type,
                      entity_name_t::MDS(-1), "mds",
                      nonce, Messenger::HAS_MANY_CONNECTIONS);
  if (!msgr)
    forker.exit(1);
  msgr->set_cluster_protocol(CEPH_MDS_PROTOCOL);

  cout << "starting " << g_conf()->name << " at " << msgr->get_myaddrs()
       << std::endl;
  uint64_t required =
    CEPH_FEATURE_OSDREPLYMUX;

  msgr->set_default_policy(Messenger::Policy::lossy_client(required));
  msgr->set_policy(entity_name_t::TYPE_MON,
                   Messenger::Policy::lossy_client(CEPH_FEATURE_UID |
                                                   CEPH_FEATURE_PGID64));
  msgr->set_policy(entity_name_t::TYPE_MDS,
                   Messenger::Policy::lossless_peer(CEPH_FEATURE_UID));
  msgr->set_policy(entity_name_t::TYPE_CLIENT,
                   Messenger::Policy::stateful_server(0));

  int r = msgr->bindv(addrs);
  if (r < 0)
    forker.exit(1);

  // get monmap
  /* monmap内容如下,记录了mon节点的ip/port,名称等信息
  fsid d60bfa64-9f2c-42c4-ad33-d964eab9e7fc
  last_changed 2019-10-10 10:16:41.683766
  created 2019-10-10 10:16:41.683766
  0: 192.168.2.41:6789/0 mon.um1
  1: 192.168.2.42:6789/0 mon.um2
  2: 192.168.2.43:6789/0 mon.um3
  具体解释见https://docs.ceph.com/docs/master/architecture/
  */
  MonClient mc(g_ceph_context);
  if (mc.build_initial_monmap() < 0)
    forker.exit(1);
  global_init_chdir(g_ceph_context);

  msgr->start();

  // start mds
  // 新建一个mds实例并初始化,至此mds常规启动完成,可接受消息并分发处理
  mds = new MDSDaemon(g_conf()->name.get_id().c_str(), msgr, &mc);

  r = mds->init();
  if (r < 0) {
    msgr->wait();
    goto shutdown;
  }

  msgr->wait();
 
 shutdown:
    // 优雅退出流程
  return 0;
}

至此,新建了一个MDSDaemon实例,MDSDaemon是mds进程的抽象,继承自Dispatcher类,负责mds和外界(其他ceph daemon、cephfs client)的交互,下面重点关注MDSDaemon的初始化

MDSDaemon Init

src/mds/MDSDaemon.cc

int MDSDaemon::init()
{
  // 注册到Messenger的订阅者列表中,ceph网络模块的代码此次不关注
  // 只要知道这里是和收发消息有关即可
  messenger->add_dispatcher_tail(&beacon);
  messenger->add_dispatcher_tail(this);

  // 初始化monclient
  r = monc->init();

  // 初始化mgrclient
  mgrc.init();
  messenger->add_dispatcher_head(&mgrc);

  // 想要订阅mdsmap和mgrmap,mdsmap用户维护状态,mgrmap还未研究
  monc->sub_want("mdsmap", 0, 0);
  monc->sub_want("mgrmap", 0, 0);
  monc->renew_subs();

  mds_lock.Unlock();

  /*
   admin socket相关,完成的工作有:
    |__创建AdminSocket类实例
    |__创建MDSSocketHook类实例
    |__调用AdminSocket->register_command()来注册MDSDaemon支持的命令
  */
  set_up_admin_socket();
  g_conf().add_observer(this);
  mds_lock.Lock();
  if (beacon.get_want_state() == MDSMap::STATE_DNE) {
    suicide();  // we could do something more graceful here
    dout(4) << __func__ << ": terminated already, dropping out" << dendl;
    mds_lock.Unlock();
    return 0; 
  }

  // 初始化SafeTimer定时器
  timer.init();

  // 初始化Beacon
  beacon.init(*mdsmap);
  // 设置Messenger名称
  messenger->set_myname(entity_name_t::MDS(MDS_RANK_NONE));

  // schedule tick
  // 开始定时器调度
  reset_tick();
  mds_lock.Unlock();

  return 0;
}

定时器

从MDSDaemon的init过程可以看到,会开启一个定时器,之后在mds的生命周期内,该定时器会不断触发并进行一些工作,这里再简单介绍下ceph中定时器的用法:

void MDSDaemon::reset_tick()
{
  // cancel old
  if (tick_event) timer.cancel_event(tick_event);

  // schedule
  // 开启定时器,当定时器时间到后调用MDSDaemon::tick()
  tick_event = timer.add_event_after(
    g_conf()->mds_tick_interval,
    new FunctionContext([this](int) {
    ceph_assert(mds_lock.is_locked_by_me());
    tick();
      }));
}

// 定时器时间到时执行该函数:先重置定时器,再调度MDSRank::tick()
// 默认定时器时间为mds_tick_interval:5s
void MDSDaemon::tick()
{
  // reschedule
  reset_tick();

  // Call through to subsystems' tick functions
  if (mds_rank) {
    mds_rank->tick();
  }
}

消息接收和处理

mds收到外部消息后通过MDSDaemon::ms_dispatch2来进行分发处理,这里又分为core msg和client msg两类。

bool MDSDaemon::ms_dispatch2(const Message::ref &m)
{
  std::lock_guard l(mds_lock);
  if (stopping) {
    return false;
  }

  // Drop out early if shutting down
  // 如果mds进程已经处于shutdown状态,则不能处理消息
  if (beacon.get_want_state() == CEPH_MDS_STATE_DNE) {
    dout(10) << " stopping, discarding " << *m << dendl;
    return true;
  }

  // First see if it's a daemon message
  // 优先处理其他daemon的msg(mon,osd等)
  const bool handled_core = handle_core_message(m);
  if (handled_core) {
    return true;
  }

  // Not core, try it as a rank message
  // 如果不是daemon msg,则交由MDSRankDispatcher::ms_dispatch()来处理
  if (mds_rank) {
    return mds_rank->ms_dispatch(m);
  } else {
    return false;
  }
}

MDS消息处理流程总结概括为下图:
MDS消息处理流程图

总结

  1. MDS的启动过程大致如下:
    1. 命令行参数解析
    2. 全局初始化工作
    3. 初始化堆栈分析器,需设置CEPH_HEAP_PROFILER_INIT的env
    4. fork子进程并等待子进程退出
    5. 创建一个Messenger实例并进行相关配置,用于消息的收发
    6. monmap的获取
    7. 新建一个MDSDaemon实例并初始化
  2. mds启动后默认开一个启定时器,间隔为mds_tick_interval(默认5s)
  3. mds的消息处理入口为ms_dispatch2
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值