RGW 对象多版本功能系统架构与代码解析

0 阅读7分钟

RGW 对象多版本功能系统架构与代码解析

基于 Ceph Reef (17.x)/Squid (18.x) 版本源码分析,核心逻辑与其他版本基本一致。


目录

  1. 多版本功能系统架构总览
  2. 多版本数据结构与存储格式
  3. 核心流程图
  4. 关键代码解析
  5. 配置与运维

1. 多版本功能系统架构总览

1.1 功能概述

Ceph RGW 的对象多版本功能遵循 S3 Object Versioning 规范,允许用户在桶中保留、检索和恢复对象的不同版本。这为数据保护提供了强大支持,特别是在配合 Object Lock 使用时可以实现对象不可变存储。

1.2 系统架构图

┌─────────────────────────────────────────────────────────────────────┐
│                        Client (S3/Swift API)                          │
│              PUT/GET/DELETE Object with Versioning Headers           │
└──────────────────────────────┬──────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────────┐
│                          RGW Gateway 进程                              │
│                                                                       │
│  ┌──────────────┐   ┌──────────────────┐   ┌────────────────────┐   │
│  │  Request     │──▶│  Versioning      │──▶│  Versioning Ops    │   │
│  │  Handler     │   │  Manager         │   │  (Put/Get/Delete)  │   │
│  └──────────────┘   └──────────────────┘   └─────────┬──────────┘   │
│                                                        │             │
│                              ┌─────────────────────────▼──────────┐ │
│                              │   RGWRados::Object (Versioning)    │ │
│                              │  ┌───────────────────────────────┐ │ │
│                              │  │ - put_obj() 带版本控制        │ │ │
│                              │  │ - get_obj() 指定版本          │ │ │
│                              │  │ - delete_obj() 版本删除       │ │ │
│                              │  └──────────────┬────────────────┘ │ │
│                              └─────────────────┼──────────────────┘ │
│                                                │                    │
│  ┌─────────────────────────────────────────────▼──────────────────┐│
│  │           Bucket Index Versioning Operations                    ││
│  │  ┌─────────────────────────────────────────────────────────┐   ││
│  │  │ cls_bucket_list_op / cls_bucket_put_op 等 RADOS 类     │   ││
│  │  │ - 版本化桶索引操作                                       │   ││
│  │  │ - 维护版本元数据                                         │   ││
│  │  └──────────────────────────────┬──────────────────────────┘   ││
│  └─────────────────────────────────┼──────────────────────────────┘│
└─────────────────────────────────┼───────────────────────────────────┘
                                  │  librados 操作
                                  ▼
┌─────────────────────────────────────────────────────────────────────┐
│                          Ceph RADOS 集群                              │
│                                                                      │
│   └── pool: default.rgw.buckets.index ← 桶索引(含版本信息)         │
│        └── 对象: .dir.{bucket_id}.{shard_id}                        │
│             └── OMAP: 对象名 → 版本ID → 元数据                       │
│                                                                      │
│   └── pool: default.rgw.buckets.data ← 实际数据对象                  │
│        └── 对象: bucket_id/obj_name.version_id                       │
└─────────────────────────────────────────────────────────────────────┘

1.3 核心组件说明

组件功能代码位置
Versioning Manager管理桶版本状态,处理版本化操作src/rgw/rgw_bucket_versioning.cc
RGWRados::Object处理版本化对象的 CRUD 操作src/rgw/rgw_rados.cc
Bucket Index存储版本化元数据,维护版本历史src/cls/rgw/cls_rgw.cc
RADOS Class实现版本化桶索引的原子操作src/cls/rgw/cls_rgw.cc

2. 多版本数据结构与存储格式

2.1 版本元数据结构

// 桶索引中的版本条目结构
struct rgw_bucket_dir_entry {
  std::string key;              // 对象名
  uint64_t size;                // 对象大小
  utime_t mtime;                // 修改时间
  std::string etag;             // ETag
  std::string content_type;     // 内容类型
  std::vector<rgw_bucket_dir_entry_meta> custom_attrs; // 自定义元数据
  
  // 版本控制相关字段
  std::string version_id;       // 版本ID
  bool is_delete_marker{false}; // 是否为删除标记
  std::string latest_version_id; // 最新版本ID
  
  void encode(bufferlist& bl) const {
    ENCODE_START(1, 1, bl);
    encode(key, bl);
    encode(size, bl);
    encode(mtime, bl);
    encode(etag, bl);
    encode(content_type, bl);
    encode(custom_attrs, bl);
    encode(version_id, bl);
    encode(is_delete_marker, bl);
    encode(latest_version_id, bl);
    ENCODE_FINISH(bl);
  }
};
// 桶版本状态
struct rgw_bucket_versioning_status {
  VersioningStatus status;       // Suspended/Enabled
  bool mfa_delete{false};       // 是否需要MFA删除
  
  void encode(bufferlist& bl) const {
    ENCODE_START(1, 1, bl);
    encode((uint32_t)status, bl);
    encode(mfa_delete, bl);
    ENCODE_FINISH(bl);
  }
};

2.2 OMAP 存储格式

Pool: default.rgw.buckets.index
Object: .dir.{bucket_id}.{shard_id}
OMAP Key: 对象名
OMAP Value: 序列化的桶目录条目(包含版本信息)
// 版本化桶索引的 OMAP 布局
"example.txt" → {
  "key": "example.txt",
  "size": 1024,
  "version_id": "v1.0",
  "is_delete_marker": false,
  "latest_version_id": "v2.0",
  // ... 其他元数据
}

3. 核心流程图

3.1 启用版本控制流程

flowchart LR
    A[客户端请求<br/>PUT Bucket Versioning] --> B[RGW 接收请求]
    B --> C[验证权限]
    C --> D[获取当前桶配置]
    D --> E{版本控制已启用?}
    E -- 是 --> F[返回成功<br/>幂等操作]
    E -- 否 --> G[更新桶元数据]
    G --> H[设置版本状态为启用]
    H --> I[更新桶索引]
    I --> J[返回成功]

3.2 版本化对象上传流程

flowchart LR
    A[客户端上传对象<br/>PUT Object] --> B[RGW 接收请求]
    B --> C[检查桶版本状态]
    C --> D{版本控制已启用?}
    D -- 否 --> E[普通上传<br/>覆盖现有对象]
    D -- 是 --> F[生成新版本ID]
    F --> G[上传数据对象<br/>到数据池]
    G --> H[更新桶索引<br/>添加新版本条目]
    H --> I[更新最新版本指针]
    I --> J[返回版本ID<br/>给客户端]

3.3 版本化对象删除流程

flowchart LR
    A[客户端删除对象<br/>DELETE Object] --> B[RGW 接收请求]
    B --> C[检查桶版本状态]
    C --> D{版本控制已启用?}
    D -- 否 --> E[永久删除对象]
    D -- 是 --> F[是否指定版本?]
    F -- 是 --> G[删除指定版本]
    G --> H[从桶索引中移除]
    F -- 否 --> I[创建删除标记]
    I --> J[更新桶索引<br/>添加删除标记]
    J --> K[返回成功]

3.4 版本列表获取流程

flowchart LR
    A[客户端请求版本列表<br/>GET Bucket Versions] --> B[RGW 接收请求]
    B --> C[检查桶版本状态]
    C --> D{版本控制已启用?}
    D -- 否 --> E[返回错误<br/>InvalidRequest]
    D -- 是 --> F[解析请求参数<br/>prefix, delimiter, max-keys]
    F --> G[遍历桶索引]
    G --> H[过滤版本条目]
    H --> I[构建响应]
    I --> J[返回版本列表]

4. 关键代码解析

4.1 版本控制管理器:src/rgw/rgw_bucket_versioning.cc

class RGWBucketVersioning {
  CephContext *cct;
  RGWRados *store;
  
public:
  RGWBucketVersioning(CephContext *_cct, RGWRados *_store)
    : cct(_cct), store(_store) {}
    
  // 启用/禁用版本控制
  int set_versioning(const DoutPrefixProvider *dpp,
                     const rgw_bucket& bucket,
                     VersioningStatus status,
                     bool mfa_delete,
                     optional_yield y) {
    // 1. 验证权限
    int ret = check_permissions(dpp, bucket, y);
    if (ret < 0) return ret;
    
    // 2. 获取桶元数据
    RGWBucketInfo bucket_info;
    ret = store->get_bucket_info(dpp, bucket, bucket_info, nullptr, y);
    if (ret < 0) return ret;
    
    // 3. 更新版本状态
    bucket_info.versioning_status = status;
    bucket_info.mfa_delete = mfa_delete;
    
    // 4. 保存桶元数据
    return store->put_bucket_instance_info(dpp, bucket_info, false, y);
  }
  
  // 获取版本状态
  int get_versioning(const DoutPrefixProvider *dpp,
                     const rgw_bucket& bucket,
                     VersioningStatus *status,
                     bool *mfa_delete,
                     optional_yield y) {
    RGWBucketInfo bucket_info;
    int ret = store->get_bucket_info(dpp, bucket, bucket_info, nullptr, y);
    if (ret < 0) return ret;
    
    if (status) *status = bucket_info.versioning_status;
    if (mfa_delete) *mfa_delete = bucket_info.mfa_delete;
    return 0;
  }
  
  // 检查是否启用版本控制
  bool is_versioning_enabled(const rgw_bucket& bucket) {
    RGWBucketInfo bucket_info;
    // 快速检查,不获取完整信息
    return bucket_info.versioning_status == VersioningStatus::Enabled;
  }
};

4.2 版本化对象操作:src/rgw/rgw_rados.cc

// 版本化对象上传
int RGWRados::Object::Write::put_obj(const DoutPrefixProvider *dpp,
                                     RGWObjectCtx& obj_ctx,
                                     const char *data,
                                     size_t len,
                                     map<string, bufferlist>& attrs,
                                     rgw_obj& target_obj,
                                     bool exclusive,
                                     const real_time& mtime,
                                     map<string, bufferlist>* pattrs,
                                     rgw_obj_manifest *manifest,
                                     const char *if_match,
                                     const char *if_nomatch,
                                     const real_time *src_mtime,
                                     const real_time *mtime,
                                     const char *owner,
                                     const char *tag,
                                     uint64_t olh_epoch,
                                     uint64_t ver_epoch) {
  // 1. 检查版本状态
  if (bucket_info.versioning_status == VersioningStatus::Enabled) {
    // 2. 生成新版本ID
    string version_id = generate_version_id();
    
    // 3. 构造版本化对象名
    rgw_obj versioned_obj = target_obj;
    versioned_obj.key.set_name(target_obj.key.name + "." + version_id);
    
    // 4. 上传版本化对象
    int ret = put_obj_versioned(dpp, obj_ctx, data, len, attrs, 
                                versioned_obj, exclusive, mtime, 
                                pattrs, manifest, ...);
    if (ret < 0) return ret;
    
    // 5. 更新桶索引
    return update_bucket_index_versioned(dpp, bucket, target_obj, 
                                         version_id, versioned_obj);
  }
  
  // 非版本化处理
  return put_obj_non_versioned(dpp, obj_ctx, data, len, attrs, 
                              target_obj, exclusive, mtime, ...);
}
// 版本化对象删除
int RGWRados::Object::Delete::delete_obj(const DoutPrefixProvider *dpp,
                                         RGWObjectCtx& obj_ctx,
                                         RGWObjVersionTracker *objv_tracker,
                                         int *http_code) {
  // 1. 检查版本状态
  if (bucket_info.versioning_status == VersioningStatus::Enabled) {
    // 2. 检查是否指定版本
    if (!version_id.empty()) {
      // 删除指定版本
      return delete_version_specific(dpp, obj_ctx, version_id, 
                                    objv_tracker, http_code);
    } else {
      // 创建删除标记
      return create_delete_marker(dpp, obj_ctx, objv_tracker, http_code);
    }
  }
  
  // 非版本化删除
  return delete_obj_non_versioned(dpp, obj_ctx, objv_tracker, http_code);
}
// 版本列表获取
int RGWRados::list_bucket_versions(const DoutPrefixProvider *dpp,
                                   const rgw_bucket& bucket,
                                   RGWBucketList& results,
                                   map<string, bool> *common_prefixes,
                                   size_t max,
                                   string& next_marker,
                                   bool *is_truncated,
                                   bool list_all_versions) {
  // 1. 验证版本控制已启用
  if (bucket_info.versioning_status != VersioningStatus::Enabled) {
    return -EINVAL; // InvalidRequest
  }
  
  // 2. 解析请求参数
  // prefix, delimiter, max-keys, version-id-marker 等
  
  // 3. 遍历桶索引,收集版本条目
  vector<rgw_bucket_dir_entry> entries;
  int ret = list_bucket_versioned(dpp, bucket, prefix, delimiter, 
                                 max, next_marker, entries, 
                                 is_truncated, list_all_versions);
  if (ret < 0) return ret;
  
  // 4. 构建响应
  results.set(entries, common_prefixes);
  return 0;
}

4.3 桶索引版本化操作:src/cls/rgw/cls_rgw.cc

// 添加版本条目到桶索引
static int cls_rgw_bucket_put_versioned(cls_method_context_t hctx,
                                        bufferlist *in, bufferlist *out) {
  // 1. 解码输入参数
  cls_rgw_bucket_put_versioned_op op;
  try {
    op.decode(*in);
  } catch (buffer::error& err) {
    return -EINVAL;
  }
  
  // 2. 获取桶索引对象
  bufferlist bl;
  int ret = cls_cxx_getxattr(hctx, "bucket.dir", &bl);
  if (ret < 0) return ret;
  
  // 3. 解码当前桶目录
  RGWBucketDirectory bucket_dir;
  try {
    bucket_dir.decode(bl);
  } catch (buffer::error& err) {
    return -EIO;
  }
  
  // 4. 添加版本条目
  rgw_bucket_dir_entry entry = op.entry;
  entry.version_id = generate_version_id();
  bucket_dir.entries[entry.key] = entry;
  
  // 5. 更新最新版本指针
  bucket_dir.latest_version_ids[entry.key] = entry.version_id;
  
  // 6. 编码并保存桶目录
  bufferlist updated_bl;
  bucket_dir.encode(updated_bl);
  ret = cls_cxx_setxattr(hctx, "bucket.dir", &updated_bl);
  
  return ret;
}
// 获取版本列表
static int cls_rgw_bucket_list_versions(cls_method_context_t hctx,
                                        bufferlist *in, bufferlist *out) {
  // 1. 解码输入参数
  cls_rgw_bucket_list_versions_op op;
  try {
    op.decode(*in);
  } catch (buffer::error& err) {
    return -EINVAL;
  }
  
  // 2. 获取桶索引对象
  bufferlist bl;
  int ret = cls_cxx_getxattr(hctx, "bucket.dir", &bl);
  if (ret < 0) return ret;
  
  // 3. 解码桶目录
  RGWBucketDirectory bucket_dir;
  try {
    bucket_dir.decode(bl);
  } catch (buffer::error& err) {
    return -EIO;
  }
  
  // 4. 过滤版本条目
  vector<rgw_bucket_dir_entry> version_entries;
  for (auto& entry : bucket_dir.entries) {
    if (op.prefix.empty() || entry.first.compare(0, op.prefix.size(), op.prefix) == 0) {
      version_entries.push_back(entry.second);
    }
  }
  
  // 5. 排序(按版本时间戳)
  std::sort(version_entries.begin(), version_entries.end(),
            [](const rgw_bucket_dir_entry& a, const rgw_bucket_dir_entry& b) {
              return a.mtime < b.mtime;
            });
  
  // 6. 截取结果
  if (version_entries.size() > op.max) {
    version_entries.resize(op.max);
    *is_truncated = true;
  }
  
  // 7. 编码响应
  cls_rgw_bucket_list_versions_ret ret;
  ret.entries = version_entries;
  ret.encode(*out);
  
  return 0;
}

5. 配置与运维

5.1 启用版本控制

# 1. 启用桶版本控制
aws s3api put-bucket-versioning --bucket my-bucket --versioning-configuration Status=Enabled
# 2. 验证版本控制状态
aws s3api get-bucket-versioning --bucket my-bucket
# 3. 查看版本列表
aws s3api list-object-versions --bucket my-bucket
# 4. 上传新版本对象
aws s3api put-object --bucket my-bucket --key example.txt --body new-content.txt
# 5. 获取特定版本对象
aws s3api get-object --bucket my-bucket --key example.txt --version-id v1.0
# 6. 删除特定版本
aws s3api delete-object --bucket my-bucket --key example.txt --version-id v1.0

5.2 配置参数

参数默认值说明
rgw_versioning_enabledfalse是否全局启用版本控制
rgw_versioning_max_versions1000每个对象最大保留版本数
rgw_versioning_retention_days30版本保留天数
rgw_versioning_mfa_deletefalse是否需要MFA删除

5.3 监控与日志

# 查看版本控制相关日志
grep -i "versioning" /var/log/ceph/ceph-client.rgw.*.log
# 监控版本数量
radosgw-admin bucket stats --bucket=my-bucket | jq '.num_versions'
# 查看特定对象的所有版本
radosgw-admin object versions --bucket=my-bucket --object=example.txt

5.4 故障排查

🔧 常见问题与解决方案 1. **版本控制未生效** - 检查桶状态:`aws s3api get-bucket-versioning --bucket=my-bucket` - 确认请求包含正确头部:`x-amz-versioning-status: Enabled` 2. **版本列表为空** - 检查版本控制是否已启用 - 确认对象有多个版本:`aws s3api list-object-versions --bucket=my-bucket` 3. **删除标记未创建** - 检查权限是否足够 - 确认版本控制状态为 `Enabled` 4. **版本数量过多导致性能问题** - 调整 `rgw_versioning_max_versions` - 使用生命周期规则清理旧版本:`aws s3api put-bucket-lifecycle-configuration`
--- ## 附:关键源码文件索引 | 文件 | 内容 | |---|---| | `src/rgw/rgw_bucket_versioning.h/.cc` | 版本控制管理器,处理桶级别版本状态 | | `src/rgw/rgw_rados.cc` | 版本化对象 CRUD 操作实现 | | `src/cls/rgw/cls_rgw.cc` | RADOS 类方法,实现桶索引的版本化操作 | | `src/rgw/rgw_op.h` | 定义版本化操作类(如 `RGWPutObj`) | | `src/rgw/rgw_rest_s3.cc` | S3 API 层面处理版本化请求 | --- *本文档可保存为 `ceph-rgw-versioning-analysis.md`。如需某一特定流程(如多站点版本同步、生命周期与版本控制交互)的详细解析,可继续展开。*