ceph rgw:lifecycle實(shí)現(xiàn)

lifecycle policy的存儲機(jī)制

當(dāng)我們?yōu)橐粋€bucket配置lifecycle policy時总寒,lifecycle相關(guān)的數(shù)據(jù)會存儲在2個位置:

  1. 在bucket.instance對象的xattr中寫入key為user.rgw.lc value為LifecycleConfig的屬性(真正的lifecycle rules列表)。
  2. 在lc.0 - lc.31共32個對象(這個值可配置)中選擇其中一個蚓炬,向其omap寫入lifecycle的狀態(tài)信息。但其實(shí)在該omap對應(yīng)的header中也有l(wèi)c相關(guān)的信息摊腋,比如用于記錄當(dāng)前omap的lifecycle遍歷進(jìn)度的marker,但這些數(shù)據(jù)不是在設(shè)置lc時設(shè)置的尤泽。

下面我們驗(yàn)證下:

首先脆淹,我們通過boto向一個名為bucket1的bucket配置lifecycle policy:

bucket1 = conn.get_bucket('bucket1')
expir = Expiration(days=1)
lc = Lifecycle()
lc.add_rule(
    prefix = "test/",
    expiration = expir,
)
bucket1.configure_lifecycle(lc)

之后签杈,去該bucket1對應(yīng)的bucket.instance對象的xattr中查看瘫镇。

$ rados -p default.rgw.meta ls --namespace root
bucket1
.bucket.meta.bucket1:38d08ed7-3883-49de-ab89-0dea7c8c960f.4162.1

$ rados -p default.rgw.meta --namespace root listxattr .bucket.meta.bucket1:38d08ed7-3883-49de-ab89-0dea7c8c960f.4162.1
ceph.objclass.version
user.rgw.acl
user.rgw.lc

user.rgw.lc對應(yīng)的value就是該bucket的lifecycle rule列表。

然后答姥,再去查看lc.xx對象

$ rados -p default.rgw.log --namespace=lc ls
lc.6
lc.14
lc.29
lc.8
lc.10
lc.26
lc.22
lc.17
lc.27
lc.4
lc.11
lc.18
lc.20
lc.7
lc.2
lc.13
lc.16
lc.12
lc.30
lc.24
lc.9
lc.15
lc.19
lc.21
lc.23
lc.31
lc.25
lc.5
lc.3
lc.28
lc.1
lc.0

RGWPutLC::execute()代碼

lifecycle的組織方式也可以在put lc操作的代碼中窺見一斑铣除。

void RGWPutLC::execute()
{
  bufferlist bl;
  
  RGWLifecycleConfiguration_S3 *config = NULL;
  RGWLCXMLParser_S3 parser(s->cct);
  RGWLifecycleConfiguration_S3 new_config(s->cct);

  // 從http header中取出md5到content_md5
  content_md5 = s->info.env->get("HTTP_CONTENT_MD5");
  if (content_md5 == nullptr) {
    op_ret = -ERR_INVALID_REQUEST;
    s->err.message = "Missing required header for this request: Content-MD5";
    ldout(s->cct, 5) << s->err.message << dendl;
    return;
  }
  // 將取出的md5從base64解碼到content_md5_bin
  std::string content_md5_bin;
  try {
    content_md5_bin = rgw::from_base64(boost::string_view(content_md5));
  } catch (...) {
    s->err.message = "Request header Content-MD5 contains character "
                     "that is not base64 encoded.";
    ldout(s->cct, 5) << s->err.message << dendl;
    op_ret = -ERR_BAD_DIGEST;
    return;
  }

  if (!parser.init()) {
    op_ret = -EINVAL;
    return;
  }
  // 從req_state中解析出put lc所需的參數(shù)存入RGWPutLC.data長度為RGWPutLC.len
  op_ret = get_params();
  if (op_ret < 0)
    return;

  ldout(s->cct, 15) << "read len=" << len << " data=" << (data ? data : "") << dendl;

  // 計算params的MD5
  MD5 data_hash;
  unsigned char data_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE];
  data_hash.Update(reinterpret_cast<const byte*>(data), len);
  data_hash.Final(data_hash_res);
  // 比較計算出的md5和客戶端傳入的md5是否一致,以判斷數(shù)據(jù)是否損壞
  if (memcmp(data_hash_res, content_md5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) {
    op_ret = -ERR_BAD_DIGEST;
    s->err.message = "The Content-MD5 you specified did not match what we received.";
    ldout(s->cct, 5) << s->err.message
                     << " Specified content md5: " << content_md5
                     << ", calculated content md5: " << data_hash_res
                     << dendl;
    return;
  }

  // 將data中的參數(shù)數(shù)據(jù)解析到parser對象中
  if (!parser.parse(data, len, 1)) {
    op_ret = -ERR_MALFORMED_XML;
    return;
  }
  // 解析出的xml對象是一顆樹結(jié)構(gòu)
  /*
  class XMLObj
  {
    XMLObj *parent;
    ......
    multimap<string, XMLObj *> children;
    ......
  }
  */
  // 如上鹦付,每一個標(biāo)簽作為一個節(jié)點(diǎn)尚粘,分別包含指向其父節(jié)點(diǎn)的指針和孩子節(jié)點(diǎn)的指針列表
  config = static_cast<RGWLifecycleConfiguration_S3 *>(parser.find_first("LifecycleConfiguration"));
  if (!config) {
    op_ret = -ERR_MALFORMED_XML;
    return;
  }
  // 將config中的rule_map中的rule轉(zhuǎn)存到new_config的rule_map和prefix_map中
  op_ret = config->rebuild(store, new_config);
  if (op_ret < 0)
    return;

  if (s->cct->_conf->subsys.should_gather(ceph_subsys_rgw, 15)) {
    ldout(s->cct, 15) << "New LifecycleConfiguration:";
    new_config.to_xml(*_dout);
    *_dout << dendl;
  }
  // 將rule_map編碼存入bl,并copy一個attrs map睁壁,增加RGW_ATTR_LC->bl項(xiàng)背苦,
  new_config.encode(bl);
  map<string, bufferlist> attrs;
  attrs = s->bucket_attrs;
  attrs[RGW_ATTR_LC] = bl;
  // 將新的attrs寫入bucket.instance對象的xattr中,
  op_ret = rgw_bucket_set_attrs(store, s->bucket_info, attrs, &s->bucket_info.objv_tracker);
  if (op_ret < 0)
    return;

  string shard_id = s->bucket.tenant + ':' + s->bucket.name + ':' + s->bucket.bucket_id;  
  string oid; 
  // 從default.rgw.log pool中的32個lc.xx對象中選擇一個潘明,構(gòu)造oid行剂,xx表示一個0-31的整數(shù)
  // get_lc_oid代碼如下:
  /*
  static void get_lc_oid(struct req_state *s, string& oid){
    string shard_id = s->bucket.name + ':' +s->bucket.bucket_id;
    int max_objs = (s->cct->_conf->rgw_lc_max_objs > HASH_PRIME)?HASH_PRIME:s->cct->_conf->rgw_lc_max_objs;
    int index = ceph_str_hash_linux(shard_id.c_str(), shard_id.size()) % HASH_PRIME % max_objs;
    oid = lc_oid_prefix;
    char buf[32];
    snprintf(buf, 32, ".%d", index);
    oid.append(buf);
    return;
  }
  */
  get_lc_oid(s, oid);
  // 構(gòu)造要寫入omap的entry內(nèi)容
  pair<string, int> entry(shard_id, lc_uninitial);
  int max_lock_secs = s->cct->_conf->rgw_lc_lock_max_time;
  rados::cls::lock::Lock l(lc_index_lock_name); 
  utime_t time(max_lock_secs, 0);
  l.set_duration(time);
  l.set_cookie(cookie);
  librados::IoCtx *ctx = store->get_lc_pool_ctx();
  do {
    op_ret = l.lock_exclusive(ctx, oid);
    if (op_ret == -EBUSY) {
      dout(0) << "RGWLC::RGWPutLC() failed to acquire lock on, sleep 5, try again" << oid << dendl;
      sleep(5);
      continue;
    }
    if (op_ret < 0) {
      dout(0) << "RGWLC::RGWPutLC() failed to acquire lock " << oid << op_ret << dendl;
      break;
    }

    // 在lc.xx對象關(guān)聯(lián)的omap中寫入entry
    op_ret = cls_rgw_lc_set_entry(*ctx, oid, entry);
    if (op_ret < 0) {
      dout(0) << "RGWLC::RGWPutLC() failed to set entry " << oid << op_ret << dendl;     
    }
    break;
  }while(1);
  l.unlock(ctx, oid);
  return;
}

lifecycle的作用機(jī)制

RGWLC類是負(fù)責(zé)執(zhí)行l(wèi)c的類,它會根據(jù)用戶的配置開啟1個或多個worker線程钳降,這些worker線程的任務(wù)是在一個無限循環(huán)中厚宰,每隔一段時間(生產(chǎn)環(huán)境是一天一次,測試環(huán)境比較頻繁)判斷一下當(dāng)前是否應(yīng)該執(zhí)行l(wèi)ifecycle的遍歷工作遂填,如果是的話铲觉,調(diào)用RGWLC類的process方法,隨機(jī)選擇32個lc.xx對象中的一個吓坚,根據(jù)其header中的標(biāo)記撵幽,取出其未遍歷的下一個omap entry,更新header中的標(biāo)記礁击,更新該entry的狀態(tài)為processing盐杂,然后處理該entry逗载,遍歷該條entry對應(yīng)的bucket中的所有對象,根據(jù)lc規(guī)則刪除或轉(zhuǎn)換bucket中過期的object链烈,并寫日志厉斟。

代碼追蹤如下:

下面這個函數(shù)是worker線程的的執(zhí)行內(nèi)容,可以看到强衡,它在一個while循環(huán)中擦秽,每隔一段時間判斷should_work,如果通過的話漩勤,那么就調(diào)用lc->process()函數(shù)進(jìn)行遍歷感挥,然后設(shè)置下一次被喚醒的時間,進(jìn)入阻塞狀態(tài)锯七。

void *RGWLC::LCWorker::entry() {
  do {
    utime_t start = ceph_clock_now();
    if (should_work(start)) {
      dout(2) << "life cycle: start" << dendl;
      int r = lc->process();
      if (r < 0) {
        dout(0) << "ERROR: do life cycle process() returned error r=" << r << dendl;
      }
      dout(2) << "life cycle: stop" << dendl;
    }
    if (lc->going_down())
      break;

    utime_t end = ceph_clock_now();
    int secs = schedule_next_start_time(start, end);
    utime_t next;
    next.set_from_double(end + secs);

    dout(5) << "schedule life cycle next start time: " << rgw_to_asctime(next) <<dendl;

    lock.Lock();
    cond.WaitInterval(lock, utime_t(secs, 0));
    lock.Unlock();
  } while (!lc->going_down());

  return NULL;
}

在RGWLC::process函數(shù)中链快,主要做了以下幾件事:
1.從lc.xx對象的header中獲得omap中要遍歷的下一個entry
2.將拿到的entry的狀態(tài)設(shè)為processing(正在處理)
3.更新header中記錄的下一個entry
4.調(diào)用bucket_lc_process函數(shù)處理當(dāng)前的entry對應(yīng)的lc規(guī)則

int RGWLC::process(int index, int max_lock_secs)
{
  rados::cls::lock::Lock l(lc_index_lock_name);
  do {
    utime_t now = ceph_clock_now();
    pair<string, int > entry;//string = bucket_name:bucket_id ,int = LC_BUCKET_STATUS
    if (max_lock_secs <= 0)
      return -EAGAIN;

    utime_t time(max_lock_secs, 0);
    l.set_duration(time);

    int ret = l.lock_exclusive(&store->lc_pool_ctx, obj_names[index]);
    if (ret == -EBUSY) { /* already locked by another lc processor */
      dout(0) << "RGWLC::process() failed to acquire lock on, sleep 5, try again" << obj_names[index] << dendl;
      sleep(5);
      continue;
    }
    if (ret < 0)
      return 0;
    // 讀取lc.xx對象的head
    string marker;
    cls_rgw_lc_obj_head head;
    ret = cls_rgw_lc_get_head(store->lc_pool_ctx, obj_names[index], head);
    if (ret < 0) {
      dout(0) << "RGWLC::process() failed to get obj head " << obj_names[index] << ret << dendl;
      goto exit;
    }

    if(!if_already_run_today(head.start_date)) {
      head.start_date = now;
      head.marker.clear();
      ret = bucket_lc_prepare(index);
      if (ret < 0) {
      dout(0) << "RGWLC::process() failed to update lc object " << obj_names[index] << ret << dendl;
      goto exit;
      }
    }
    // 從lc.xx對象的header中獲取下一個要遍歷的omap entry
    ret = cls_rgw_lc_get_next_entry(store->lc_pool_ctx, obj_names[index], head.marker, entry);
    if (ret < 0) {
      dout(0) << "RGWLC::process() failed to get obj entry " << obj_names[index] << dendl;
      goto exit;
    }

    if (entry.first.empty())
      goto exit;
    // 將該entry的狀態(tài)設(shè)為processing
    entry.second = lc_processing;
    ret = cls_rgw_lc_set_entry(store->lc_pool_ctx, obj_names[index],  entry);
    if (ret < 0) {
      dout(0) << "RGWLC::process() failed to set obj entry " << obj_names[index] << entry.first << entry.second << dendl;
      goto exit;
    }
    // 更新header中的下一個entry標(biāo)記
    head.marker = entry.first;
    ret = cls_rgw_lc_put_head(store->lc_pool_ctx, obj_names[index],  head);
    if (ret < 0) {
      dout(0) << "RGWLC::process() failed to put head " << obj_names[index] << dendl;
      goto exit;
    }
    l.unlock(&store->lc_pool_ctx, obj_names[index]);
    // 處理當(dāng)前的entry對應(yīng)的lc規(guī)則
    ret = bucket_lc_process(entry.first);
    bucket_lc_post(index, max_lock_secs, entry, ret);
  }while(1);

exit:
    l.unlock(&store->lc_pool_ctx, obj_names[index]);
    return 0;
}

bucket_lc_process函數(shù)做的則就是最終的處理工作了:遍歷某條lc規(guī)則對應(yīng)的bucket的所有objects誉己,根據(jù)prefix和tagging找到lc 規(guī)則作用的object眉尸,然后判斷這些objects是否過期,如果過期巨双,做對應(yīng)的刪除處理噪猾。

要注意的是,目前L版本的ceph僅支持到期刪除的lifecycle筑累,也就是Expiration袱蜡。不支持Transition。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末慢宗,一起剝皮案震驚了整個濱河市坪蚁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌镜沽,老刑警劉巖敏晤,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缅茉,居然都是意外死亡嘴脾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門蔬墩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來译打,“玉大人,你說我怎么就攤上這事拇颅∽嗨荆” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵樟插,是天一觀的道長韵洋。 經(jīng)常有香客問我哥谷,道長,這世上最難降的妖魔是什么麻献? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任们妥,我火速辦了婚禮,結(jié)果婚禮上勉吻,老公的妹妹穿的比我還像新娘监婶。我一直安慰自己,他們只是感情好齿桃,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布惑惶。 她就那樣靜靜地躺著,像睡著了一般短纵。 火紅的嫁衣襯著肌膚如雪带污。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天香到,我揣著相機(jī)與錄音鱼冀,去河邊找鬼。 笑死悠就,一個胖子當(dāng)著我的面吹牛千绪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播梗脾,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼荸型,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炸茧?” 一聲冷哼從身側(cè)響起瑞妇,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎梭冠,沒想到半個月后辕狰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妈嘹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年柳琢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片润脸。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡柬脸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毙驯,到底是詐尸還是另有隱情倒堕,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布爆价,位于F島的核電站垦巴,受9級特大地震影響媳搪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜骤宣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一秦爆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧憔披,春花似錦等限、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锰霜,卻和暖如春筹误,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背癣缅。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工厨剪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人所灸。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓丽惶,卻偏偏與公主長得像炫七,于是被迫代替她去往敵國和親爬立。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉万哪,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,686評論 0 9
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理侠驯,服務(wù)發(fā)現(xiàn),斷路器奕巍,智...
    卡卡羅2017閱讀 134,628評論 18 139
  • 第一章 Nginx簡介 Nginx是什么 沒有聽過Nginx吟策?那么一定聽過它的“同行”Apache吧!Ngi...
    JokerW閱讀 32,646評論 24 1,002
  • 直到你告訴我你要去實(shí)習(xí)了的止,我才發(fā)現(xiàn)原來一轉(zhuǎn)眼我們都認(rèn)識那么多年了檩坚。也不知道是怎么了眼淚就是不聽話的一直往下掉,止也...
    什么難閱讀 319評論 0 1
  • 那天回到家诅福,媽媽說匾委,她上午去小賣店買東西時,店主說她拿的錢是假鈔氓润,她一邊拿錢給我看赂乐,一邊憂心忡忡說:“我的錢都是從...
    莊海瓊閱讀 235評論 0 0