1. 程式人生 > >ceph的pool建立流程--程式碼分析

ceph的pool建立流程--程式碼分析

ceph中的pool有兩種型別:replicated pool和erasure pool
這裡從rados介面開始跟蹤分析一下pool的建立過程


一、rados介面部分
1、rados裡建立pool的介面是(librados.cc)
int librados::Rados::pool_create(const char *name);
int librados::Rados::pool_create(const char *name, uint64_t auid);
int librados::Rados::pool_create(const char *name, uint64_t auid, __u8 crush_rule);
int librados::Rados::pool_create_async(const char *name, PoolAsyncCompletion *c);
int librados::Rados::pool_create_async(const char *name, uint64_t auid, PoolAsyncCompletion *c);
int librados::Rados::pool_create_async(const char *name, uint64_t auid, __u8 crush_rule,
      PoolAsyncCompletion *c);

這些介面最終是會呼叫librados::RadosClient下的響應介面,比如
int librados::Rados::pool_create(const char *name, uint64_t auid, __u8 crush_rule);
呼叫的是
int librados::RadosClient::pool_create(string& name, unsigned long long auid,
      int16_t crush_rule);
這裡就以這個介面為例跟蹤


2、int librados::RadosClient::pool_create(string& name, unsigned long long auid,
      int16_t crush_rule)

   A、先是獲取osdmap,獲取失敗後,則返回
   int librados::RadosClient::wait_for_osdmap()
   這裡面是從radosclient的objecter裡讀取osdmap,如果沒有osdmap,那麼會等待objecter獲取到osdmap,直到超時(預設一直等OPTION(rados_mon_op_timeout, OPT_DOUBLE, 0))
   B、呼叫reply = objecter->create_pool(name, onfinish, auid, crush_rule);
       開始建立pool,並設定了回撥函式onfinish
   C、如果上面的函式呼叫成功,則會一直等待建立的結果
3、int create_pool(string& name, Context *onfinish, uint64_t auid=0,

 int crush_rule=-1);



4、void Objecter::pool_op_submit(PoolOp *op)
{
  // rwlock is locked
  if (mon_timeout > timespan(0)) {
    op->ontimeout = timer.add_event(mon_timeout,
   [this, op]() {
     pool_op_cancel(op->tid, -ETIMEDOUT); });
  }
  _pool_op_submit(op);
}


5、void Objecter::_pool_op_submit(PoolOp *op)
構建與mon通訊的物件,然後傳送訊息給monitor,請求建立建立pool
monc->send_mon_message(m);
訊息的型別為CEPH_MSG_POOLOP




二、Monitor訊息處理部分

monitor接收訊息後,訊息處理流程如下




CEPH_MSG_POOLOP是由OSDMonitor處理的
    case CEPH_MSG_POOLOP:
      paxos_service[PAXOS_OSDMAP]->dispatch(op);  //這裡不應答客戶端?
      break;


1、bool PaxosService::dispatch(MonOpRequestRef op)
   A、首先會丟棄舊訊息
   
   
   B、確認osdmap是最新的
   
   m->version指的是客戶端的osdmap的版本,如果m->version比本地的版本要新,則會通過paxos協議獲取最新的osdmap版本
   wait_for_readable將當前的操作OP新增到proposal佇列裡,獲取成功後,會通過回撥函式重試
   

   C、preprocess_query是查詢操作,在這裡屬於更新操作,所以與這個函式無關,這部分可以先學習paxos的內容
   D、更新操作,prepare_update()的實現是bool OSDMonitor::prepare_update(MonOpRequestRef op)
   該函式裡呼叫bool OSDMonitor::prepare_pool_op(MonOpRequestRef op)
   
   然後函式裡判斷是建立pool操作,進一步呼叫prepare_pool_op_create
   
2、bool OSDMonitor::prepare_pool_op_create(MonOpRequestRef op)


prepare_new_pool函式根據引數,設定crush_ruleset,pg_num,pgp_num,副本數,以及糾刪池的條頻寬度等
在設定完上面的引數後,會到pending_inc.new_pool_names (OSDMap::Incremental pending_inc;)裡查詢,是否已經存在相同名字的pool,如果有則返回,不重複建立,否則,建立一個新的pool物件,並新增的pending_inc.new_pool_names裡,一遍後續提交,最後為新的pool新增一些引數




在所有引數都設定完成後,需要commit當前的資訊到各個monitor節點
wait_for_finished_proposal(op, new OSDMonitor::C_PoolOp(this, op, err, pending_inc.epoch));
上面的函式將操作壓入paxos 的proposal佇列,以便提交
在回撥函式裡,會根據提交結果,應答客戶端




3、然後又回到了PaxosService::dispatch(MonOpRequestRef op)函式裡,
prepare_update(op)根據返回值,決定是否需要觸發paxos流程:




4、上面的propose_pending的實現是void PaxosService::propose_pending()
void PaxosService::propose_pending()的實現如下
   A、建立一個paxos事務
   MonitorDBStore::TransactionRef t = paxos->get_pending_transaction();
   B、呼叫encode_pending函式實現序列化,實際上是由void OSDMonitor::encode_pending(MonitorDBStore::TransactionRef t)實現
   encode_pending函式裡會先拷貝一份當前的osdmap,然後將pending_inc(即OSDMap::Incremental pending_inc;)裡的內容應用到拷貝出來的新的osdmap,生成一份新的完整的OSDMap
   
   同時將版本號,和最後提交的序號序列化進入事務裡
   C、最後應用到paxos裡面去
   // apply to paxos
  proposing = true;
  paxos->queue_pending_finisher(new C_Committed(this));
  paxos->trigger_propose();
  
  這裡回撥函式C_Committed(this)裡會呼叫void PaxosService::_active()
  最終會呼叫finish_contexts(g_ceph_context, waiting_for_finished_proposal, 0);喚醒建立pool的op操作(在prepare_pool_op_create函式裡設定了wait_for_finished_proposal(op, new OSDMonitor::C_PoolOp(this, op, err, pending_inc.epoch));)
  OSDMonitor::C_PoolOp的回撥函式回根據paxos的結果,應答客戶端
  
  

  至此,pool建立流程接收。

簡單流程總結如下