ceph源码 scrub流程解析

一、ceph scrub介绍

    ceph通过scrub保证数据的一致性,scrub 以PG 为单位,对于每一个pg,ceph 分析该pg下的object, 产生一个类似于元数据信息摘要的数据结构,如对象大小,属性等,叫scrubmap, 比较主与副scrubmap,来保证是不是有object 丢失或者不匹配。lightscrub(daily)比较object size 和属性。deep scrub (weekly)读取数据部分并通过checksum(这里是CRC32)比较保证数据一致性。 每次scrub 只取chunk(chunk大小可以通过ceph的配置选项进行配置)数量的object比较,这期间被校验对象的数据是不能被修改的,所以write请求会被block. scrub操作可以手动触发,也会根据配置项和系统负载情况每天定时触发。

二、代码流程分析

    1、手动触发scrub

        a、mon接收到scurb命令进入PGMonitor::preprocess_query函数

case MSG_PGSTATS:
    return preprocess_pg_stats(op);

  case MSG_MON_COMMAND:    //scrub
    return preprocess_command(op);

    b、preprocess_command(op)函数在mon/PGMonitor.cc文件中,给主osd发送scrub指令

        

 else if (prefix == "pg scrub" ||
             prefix == "pg repair" ||
             prefix == "pg deep-scrub") {
   .........
    mon->try_send_message(new MOSDScrub(mon->monmap->fsid, pgs,   //给主osd发送scrub指令
                                        scrubop == "repair",
                                        scrubop == "deep-scrub"),
                          mon->osdmon()->osdmap.get_inst(osd));
    ss << "instructing pg " << pgid << " on osd." << osd << " to " << scrubop;
    r = 0;
  

    c、主osd,接收到MOSDScrub的命令后,在OSD::ms_dispatch函数中调用_dispatch(m);

  do_waiters();
  _dispatch(m);  //scrub
  do_waiters();

    d、在_dispatch(m)中调用handle_scrub进行处理

  case MSG_OSD_SCRUB:
    handle_scrub(static_cast<MOSDScrub*>(m));
    break;

    e、在handle_scrub中调用pg->reg_next_scrub()注册要处理的pg,初始化了ScrubJob,ScrubJob中有任务执行需要的信息。

	if (pg->is_primary()) {
	  pg->unreg_next_scrub();
	  pg->scrubber.must_scrub = true;
	  pg->scrubber.must_deep_scrub = m->deep || m->repair;
	  pg->scrubber.must_repair = m->repair;
	  pg->reg_next_scrub();

    f、在osd初始化的时候注册了一个定时任务

  tick_timer.add_event_after(cct->_conf->osd_heartbeat_interval, new C_Tick(this));
  {
    Mutex::Locker l(tick_timer_lock);
    tick_timer_without_osd_lock.add_event_after(cct->_conf->osd_heartbeat_interval, new C_Tick_WithoutOSDLock(this));
  }

    g、该定时任务会每隔osd_heartbeat_interval时间段,就会触发定时器回调函数OSD::tick_without_osd_lock()

    h、在回调函数中调用 sched_scrub();

  if (!scrub_random_backoff()) {
    sched_scrub();
  }

    i、在OSD::sched_scrub();中主要检查时间和系统的负载,并取得ScrubJob中的第一个任务。

	if (pg->sched_scrub()) {   //调用PG::sched_scrub()
	  pg->unlock();
	  break;

    j、在PG::sched_scrub()中完成资源的预约,调用bool PG::queue_scrub()最后加入OpWq队列.

    scrub_queued = true;
    osd->queue_for_scrub(this);   //osd是OSDService类

    k、在OSDService::queue_for_scrub中加入OpWq队列

void queue_for_scrub(PG *pg) {
    op_wq.queue(
      make_pair(
	pg,
	PGQueueable(
	  PGScrub(pg->get_osdmap()->get_epoch()),
	  cct->_conf->osd_scrub_cost,
	  pg->get_scrub_priority(),
	  ceph_clock_now(cct),
	  entity_inst_t())));
  }

    l、op_wq队列在OSDService类中定义,在OSDService初始化的时候op_wq初始化OSD::op_shardedwq

   419   ShardedThreadPool::ShardedWQ < pair <PGRef, PGQueueable> > &op_wq;

    在osd/OSD.cc中

     216   op_wq(osd->op_shardedwq),

    m、op_shardedwq队列绑定OSD:: ShardedThreadPool osd_op_tp线程池。

     1669   op_shardedwq(
     1670     cct->_conf->osd_op_num_shards,
     1671     this,
     1672     cct->_conf->osd_op_thread_timeout,
     1673     cct->_conf->osd_op_thread_suicide_timeout,
     1674     &osd_op_tp),

    n、最后线程池调用pg->scrub处理

    pg scrub处理和副本osd处理,流程参考:https://my.oschina.net/linuxhunter/blog/681050

                                                            https://blog.csdn.net/younger_china/article/details/75450607

猜你喜欢

转载自my.oschina.net/u/2326998/blog/1787032