ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

OSD的状态转化

2021-07-02 12:33:48  阅读:255  来源: 互联网

标签:状态 调用 boot 转化 start STATE OSD


目录

简介

本文基于Luminous版本,分析一下OSD各状态的含义和状态转化关系。OSD的状态类型定义在osd_state_t,共有如下几种状态:

typedef enum {
    STATE_INITIALIZING = 1,
    STATE_PREBOOT,
    STATE_BOOTING,
    STATE_ACTIVE,
    STATE_STOPPING,
    STATE_WAITING_FOR_HEALTHY
} osd_state_t;

对于这些状态,其意义和出现的位置见下表,后文将对每个状态进行详细分析。

状态 意义 位置
STATE_INITIALIZING OSD初始状态;新建OSD对象后,state的默认值。 class OSD
STATE_PREBOOT OSD准备初始化;在OSD::start_boot中发送get_version消息之前设置。 OSD::start_boot
STATE_BOOTING OSD正在初始化,在get_version流程结束后,调用回调在OSD::_send_boot中,发送MOSDBoot消息之后设置。 OSD::_send_boot
STATE_ACTIVE OSD变为active状态。 OSD::_committed_osd_maps
STATE_STOPPING OSD开始关闭。 OSD::shutdown
STATE_WAITING_FOR_HEALTHY OSD等待心跳健康。 OSD::start_waiting_for_healthy

STATE_PREBOOT

STATE_INITIALIZING作为新建OSD对象后的初试状态,STATE_PREBOOT才是真正意义上的初试状态。当执行OSD::start_boot时将OSD状态设置为STATE_PREBOOT。首先梳理一下OSD::start_boot在哪些地方调用:

  • OSD::init
  • OSD::tick :tick线程对应了OSD中的SafeTimer tick_timer
  • OSD::ms_handle_connect
  • OSD::_committed_osd_maps

首先分析一下OSD::start_boot的流程,然后再分析一下调用OSD::start_boot的逻辑。

OSD::start_boot

当OSD初始化时,调用OSD::start_boot进入boot流程。函数具体流程为:

  • 处理如果OSD不处于健康的状态的情况,详情参考后文STATE_WAITING_FOR_HEALTHY
  • 通过monclient向mon发送get_version的消息,获取OSDMap的版本信息,完成后执行回调函数OSD::_got_mon_epochs
  • OSD::_got_mon_epochs调用了OSD::_preboot
    • 调用OSD::heartbeat ,目的是确认容量状态,不会让一个已经标记了full状态的down osd进入boot流程。关于心跳检测可以参考我另外一片blog。​

    • OSDMap相关的检测:

      • epoch是否为0
      • OSD是否有CEPH_OSDMAP_NOUP标记,有该标记的OSD不能进入up状态。
      • 版本相关信息
      • 判断full信息是否需要更新(通过实际状态和OSDMap中的记录对比),需要的话想mon发送MOSDFull消息。
      • 版本检查通过进入OSD::_send_boot
    • 如果没有成功进入OSD::_send_boot,调用osdmap_subscribe对OSDMap进行更新,之前只获取了版本号相关的信息。

Q:更新了OSDMap之后呢?
A:在OSD::tick线程中会重新调用OSD::start_boot

image

OSD::init中调用

OSD::init是OSD启动流程中最主要的函数,在末尾部分会调用**OSD::start_boot**,可以参考我的另一篇blog,这里不做赘述。

OSD::tick中调用

OSD::tick函数的具体流程为:

  • 如果是STATE_ACTIVE或者STATE_WAITING_FOR_HEALTHY,调用OSD::maybe_update_heartbeat_peers更新heartbeat peer。
  • 如果是STATE_PREBOOT或者STATE_WAITING_FOR_HEALTHY调用**OSD::start_boot**
  • 调用OSD::do_waitersfinished中的op进行dispatch。相关内容可以参考我的另一篇blog。

OSD::ms_handle_connect中调用

OSD::ms_handle_connect作为一个继承Dispatcher需要复写的函数,调用时机为:

  • 连接刚建立
  • 连接重新连接

即别的通信组件和OSD刚建立连接或者重连的时候,这两种情况也需要调用**OSD::start_boot**,使OSD进入Boot流程。具体的逻辑为:

  • 如果OSD处于STATE_PREBOOT状态将调用**OSD::start_boot**
  • 如果OSD处于STATE_BOOTING状态说明正在boot的过程中,此时调用OSD::_send_boot

OSD::_committed_osd_maps中调用

OSD::_dispatch中收到OSDMap类型的消息时调用OSD::handle_osd_map,将OSDMap本地化事物生成后会注册两个回调:

store->queue_transaction(
    service.meta_osr.get(),
    std::move(t),
    new C_OnMapApply(&service, pinned_maps, last),
    new C_OnMapCommit(this, start, last, m), 0);

queue_transaction完成后会调用这两个回调类中的finish函数:

  • 前者调用OSDService::clear_map_bl_cache_pins清理map_bl_inc_cachemap_bl_cache的缓存。关于这两个缓存可以查看另一篇blog关于OSDMap处理部分。
  • 后者调用OSD::_committed_osd_maps做新OSDMap相关的处理。

了解了OSD::_committed_osd_maps的调用时机,该函数主要进行了三个判断:

  • OSD需不需要关闭
  • OSD需不需要重启
  • 是否有网络错误

这里受限于篇幅原因,只分析和OSD::start_boot相关的内容。

if (do_shutdown) {
    ...
  }
  else if (m->newest_map && m->newest_map > last) {
    ...
  }
  else if (is_preboot()) {
    if (m->get_source().is_mon())
      _preboot(m->oldest_map, m->newest_map);
    else
      start_boot();
  }
  else if (do_restart)
    start_boot();

可以看出

  • 如果OSD不需要关闭且在STATE_PREBOOT状态。
    • 如果该OSDMap消息是来自mon,则进入OSD::_preboot函数。因为已经有了来自mon的最新OSDMap,无需通过上述的get_version去获取OSDMap epoch,直接进入OSD::_preboot函数。
    • 如果该OSDMap消息是来自osd,则进入OSD::start_boot
  • 如果OSD需要重启,也进入OSD::start_boot

STATE_BOOTING

OSD::_send_boot中设置STATE_BOOTING状态,接上文的OSD::start_boot之后。主要功能为:

  • 获取各类addr,为下一步做准备。
  • 向mon发送MOSDBoot消息。

STATE_STOPPING

OSD::shutdown中设置STATE_STOPPING状态,表明OSD处于正在关闭的状态。
在OSDService中还有几个和关闭相关的状态:主要是和OSDService的关闭状态有关。

enum {
    NOT_STOPPING,
    PREPARING_TO_STOP,
    STOPPING 
};
  • NOT_STOPPING为默认值
  • OSDService::prepare_to_stop向mon发送MOSDMarkMeDown类型的消息(要求ack):
    • 在发送之前设置为PREPARING_TO_STOP状态。
    • 发送之后且is_stopping_cond Signal后(在OSDService::got_stop_ack中收到ack回复后)设置为STOPPING状态。

Q:OSDService::prepare_to_stop何时调用?
A:在L版中是在OSD::shutdown中,主要作用是给mon发消息。
Q:在日志中怎么搜索这种情况?
A:搜索【telling mon we are shutting down】
Q:这些状态和OSD的的STATE_STOPPING有什么关系?
A:这三个状态主要用来维护OSDService发送消息的流程,和OSD状态没有太大关系。
Q:为什么关闭后要给mon发送消息?
A:需要判断能否mark down,修改OSDMap等工作。详细可以查看OSDMonitor::preprocess_mark_me_downOSDMonitor::prepare_mark_me_down。关于Mon的消息处理和同步可以参考我另外一篇blog

STATE_ACTIVE

STATE_ACTIVE表明OSD变为active状态。具体代码在OSD::_committed_osd_maps中:

epoch_t _bind_epoch = service.get_bind_epoch();
// OSDMap中本OSD为up
  if (osdmap->is_up(whoami) &&
// OSD为新起的OSD
    osdmap->get_addr(whoami) == client_messenger->get_myaddr() &&
    _bind_epoch < osdmap->get_up_from(whoami)) {

  if (is_booting()) {
    dout(1) << "state: booting -> active" << dendl;
      // 设置状态
    set_state(STATE_ACTIVE);

    // set incarnation so that osd_reqid_t's we generate for our
    // objecter requests are unique across restarts.
    service.objecter->set_client_incarnation(osdmap->get_epoch());
  }
}

STATE_WAITING_FOR_HEALTHY

STATE_WAITING_FOR_HEALTHY状态的含义为等待心跳健康的阶段,在OSD::start_waiting_for_healthy中设置,关于心跳检测可以参考我的另外一篇blog
如何定义健康?在OSD::start_boot中对是否健康进行了判断,如果在OSD boot的过程中还处于不健康的状态,不进行boot后续操作。

if (!_is_healthy()) {
    // if we are not healthy, do not mark ourselves up (yet)
    dout(1) << "not healthy; waiting to boot" << dendl;
    if (!is_waiting_for_healthy())
      start_waiting_for_healthy();
    // send pings sooner rather than later
    heartbeat_kick();
    return;
  }

可以看到判断的关键在OSD::_is_healthy 函数,在该函数中有两个判断点:

  • 通过HeartbeatMap::_check检查所有心跳线程是否超时。
  • 检查所有的Heartbeat_peers成员是否健康,如果满足一下任意一个条件则为不健康:
    • 没有收到心跳前peer或后peer的回复。
    • ping_history不为空(说明不是没有发送ping消息或者已经接受了所有回复的情况)且现在的时间now已经大于ping_historyoldest_deadline时间。

总结

OSD的状态变化和PG相比相对来说比较简单,本文着重分析了流程和主要函数,理解状态变化对理解OSD是至关重要的。

标签:状态,调用,boot,转化,start,STATE,OSD
来源: https://www.cnblogs.com/wujiajun1997/p/14962725.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有