bucket对象回收lifecycle机制
针对功能,对照代码,理解radosgw的lc机制
在radosgw中,可以针对bucket设置其对象的生命周期,当对象存活时间超出这个时间后,rgw会对其进行删除回收,这里设置的粒度目前是在bucket,也就是不能针对object设置其失效时间
bucket的lc机制弊端是很明显的,在lc打开的情况下,进行lc的做法是遍历整个bucket的对象,找出超过存活时间的对象进行回收,这就意味着,如果单个bucket对象数量十分庞大,这个遍历就会极其消耗性能
首先,与lc相关的参数有
"rgw_enable_lc_threads": "true",
"rgw_lc_debug_interval": "-1",
"rgw_lc_lock_max_time": "60",
"rgw_lc_max_objs": "32",
"rgw_lc_max_rules": "1000"
"rgw_lifecycle_work_time": "00:00-06:00"
默认情况下,bucket的属性标识中对象是永不过期的,对于某个bucket,手动设置其过期时间的接口,使用boto3的做法是
#pip install boto3 -i https://pypi.tuna.tsinghua.edu.cn/simple boto
from boto3.session import Session
import boto3
import uuid
access_key = 'xxx'
secret_key = 'xxxxx'
host = 'http://ip:7480'
session = Session(access_key, secret_key)
buckets = ['bucket1']
s3_client = session.client('s3', endpoint_url=host)
for x in buckets:
s3_client.put_bucket_lifecycle(
Bucket=x,
LifecycleConfiguration={
'Rules': [
{
'Expiration':
{
'Days': 1
},
'ID': str(uuid.uuid1()),
'Status': 'Enabled',
'Prefix': ''
}
],
})
print s3_client.get_bucket_lifecycle(Bucket=x)
设置完成后,可以通过命令radosgw-admin lc list
查看到当前lc的队列
[store@server227 build]$ ./bin/radosgw-admin -k keyring -c ./ceph.conf lc list
[
{
"bucket": ":cpp-bucket1:eace8f62-b901-47ed-b18f-fe2441f33830.4155.1",
"status": "COMPLETE"
}
]
代码跟读
lc处理流程的入口是rgw/rgw_lc.cc
的*RGWLC::LCWorker::entry
函数
void *RGWLC::LCWorker::entry() {
do {
utime_t start = ceph_clock_now();
//rgw_lc_debug_interval > 0时或者达到设定的回收时间则返回true
if (should_work(start)) {
ldout(cct, 2) << "life cycle: start" << dendl;
int r = lc->process();
if (r < 0) {
ldout(cct, 0) << "ERROR: do life cycle process() returned error r=" << r << dendl;
}
ldout(cct, 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);
ldout(cct, 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;
}
如果使能了lc的功能,lc的线程会在进行一次lc作业后,等待一段时间,再唤醒起来进行下一次的作业,这里函数的第三行,使用should_work
函数判断本次的lc是否进行,默认rgw_lc_debug_interval
为-1,表示不进行lc的作业,此时会进一步判断是否在设定的回收时间内,如果不在,也不进行lc作业
if (cct->_conf->rgw_lc_debug_interval > 0) {
/* We're debugging, so say we can run */
return true;
} else if ((bdt.tm_hour*60 + bdt.tm_min >= start_hour*60 + start_minute) &&
(bdt.tm_hour*60 + bdt.tm_min <= end_hour*60 + end_minute)) {
return true;
} else {
return false;
}
跟着lc->process()
看下
int RGWLC::process()
{
int max_secs = cct->_conf->rgw_lc_lock_max_time;
const int start = ceph::util::generate_random_number(0, max_objs - 1);
for (int i = 0; i < max_objs; i++) {
int index = (i + start) % max_objs;
int ret = process(index, max_secs);
if (ret < 0)
return ret;
}
return 0;
}
函数比较简短,限制每次作业的时间为rgw_lc_lock_max_time
,默认是60s,结合实际测试看,这个时间并不准确,继续看
int RGWLC::process(int index, int max_lock_secs)
{
//根据max_lock_secs来判断一个执行周期的时间长度
rados::cls::lock::Lock l(lc_index_lock_name);
do {
utime_t now = ceph_clock_now();
......
//从这里开始,正式开始进行对象的lc操作,注意到这是在一个死循环里
ret = bucket_lc_process(entry.first);
bucket_lc_post(index, max_lock_secs, entry, ret);
}while(1);
lock的对象是lc_process
,这个函数中会多次请求osd进行lc的前期操作,包括查询head、set一些配置等,最重要的是检查bucket是否配置了lc,开始lifecycle到调用bucket_lc_process
前,花费时间为68ms
,接下来
int RGWLC::bucket_lc_process(string& shard_id)
{
//对bucket的对象进行回收的操作主要在这个函数中进行
....
//设定运行周期
l.set_duration(time);
....
do {
objs.clear();
list_op.params.marker = list_op.get_next_marker();
//从bucket中选择1000个对象,使用的是bucket list方法
ret = list_op.list_objects(1000, &objs, NULL, &is_truncated);
if (ret < 0) {
if (ret == (-ENOENT))
return 0;
ldout(cct, 0) << "ERROR: store->list_objects():" <<dendl;
return ret;
}
bool is_expired;
//遍历这1000个对象,检查它们是否达到了过期时间
for (auto obj_iter = objs.begin(); obj_iter != objs.end(); ++obj_iter) {
rgw_obj_key key(obj_iter->key);
....
if (prefix_iter->second.expiration_date != boost::none) {
//we have checked it before
is_expired = true;
} else {
//检查对象是否达到过期时间
is_expired = obj_has_expired(obj_iter->meta.mtime, prefix_iter->second.expiration);
}
if (is_expired) {
int ret = store->get_obj_state(&rctx, bucket_info, obj, &state, false);
if (ret < 0) {
return ret;
}
if (state->mtime != obj_iter->meta.mtime) {
//Check mtime again to avoid delete a recently update object as much as possible
ldout(cct, 20) << __func__ << "() skipping removal: state->mtime " << state->mtime << " obj->mtime " << obj_iter->meta.mtime << dendl;
continue;
}
ret = remove_expired_obj(bucket_info, obj_iter->key, obj_iter->meta.owner, obj_iter->meta.owner_display_name, true);
if (ret < 0) {
ldout(cct, 0) << "ERROR: remove_expired_obj " << dendl;
} else {
ldout(cct, 2) << "DELETED:" << bucket_name << ":" << key << dendl;
}
if (going_down())
return 0;
}
}
} while (is_truncated);
}
在这个函数中,首先设定运行时间,线程通过bucket list的方式拿到1000个obj,获取使用按顺序的方式,遍历这1000个对象,如果该对象超过了过期时间,则删掉,关于循环的停止条件,is_truncated: if number of objects in the bucket is bigger than max, then truncated
,即要求遍历完整个bucket
这部分耗时主要依赖于bucket内对象的数量,对象越多,则耗时肯定会越多
最后,针对对象中分片情况,进行了处理,在RGWLC::bucket_lc_process
的最后
ret = handle_multipart_expiration(&target, prefix_map);
return ret;
其中prefix_map
表明了对象分片的相关信息
看下对于分片的处理逻辑
for (auto prefix_iter = prefix_map.begin(); prefix_iter != prefix_map.end(); ++prefix_iter) {
......
do {
objs.clear();
list_op.params.marker = list_op.get_next_marker();
ret = list_op.list_objects(1000, &objs, NULL, &is_truncated);
......
} while(is_truncated);
}
可以看到,与对象删除一样,分片删除也是一次性取出1000个分片对象进行删除
逻辑总结
首先,在默认情况下,lc线程启动后,根据rgw_lc_debug_interval
及设定的rgw_lifecycle_work_time
来判断要不要做本次的process,默认rgw_lc_debug_interval
为-1,表示不做本次lc,另外,在rgw_lc_debug_interval <=0
时,这个时间间隔单位将放大为天,原注释说的是We're in debug mode; Treat each rgw_lc_debug_interval seconds as a day
在决定要做本次lc后,根据rgw_lc_lock_max_time
来判断每次lc的周期时间长度,然后每次从rgw_lc_max_objs
中选取一个进行运行,增大这个值不会加快回收速度
关于rgw_lc_max_objs
,它表示的是lc的处理线程的最大数量,它并不是并发的线程,因而增大这个值并不能加快回收速度,它类似于一个分组,bucket被设置lc规则后将被分配到某个lc中,例如,这个值设置为3,它启动回收的时候会随机启动lc.0、lc.1、lc.2中的其中一个,测试发现,多个bucket都设置过期的情况下,单个rgw同一时间仅运行一个lc线程,因而设置自动回收速度其实是很慢的
接下来,请求并设定一些基本的元数据信息,最重要的是判断bucket是否需要做lc,它检测bucket是否进行了lc的设置,如果设置才做,没有设置,默认是不做
然后,从指定的bucket中每次取出最多1000个有序对象,遍历这些对象并检查他们是否到达过期时间,过期则删除,直到bucket中所有的对象都遍历完成.
针对分片做了特殊处理,删除掉第一个片后,其余分片有专门的逻辑进行进一步的删除
环境中起2个rgw进行测试,发现2个rgw可以同时进行不同bucket的回收,因而,要加快回收速度,只能通过增加rgw的数量实现,单个rgw明显只能串行单线程进行单bucket的回收,不过,因为回收bucket涉及到bucket list,过快的回收速度也可能引发性能问题,需要权衡
- 本文作者:奋斗的松鼠 - tanweijie_2012@163.com
- 本文链接: http://www.strugglesquirrel.com/2019/07/07/bucket对象回收lifecycle机制/
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!
- 特别说明: