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

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 系统架构图

sql 复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                        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 版本元数据结构

cpp 复制代码
// 桶索引中的版本条目结构
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 存储格式

javascript 复制代码
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

cpp 复制代码
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

cpp 复制代码
// 版本化对象上传
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

cpp 复制代码
// 添加版本条目到桶索引
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 启用版本控制

bash 复制代码
# 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_enabled false 是否全局启用版本控制
rgw_versioning_max_versions 1000 每个对象最大保留版本数
rgw_versioning_retention_days 30 版本保留天数
rgw_versioning_mfa_delete false 是否需要MFA删除

5.3 监控与日志

bash 复制代码
# 查看版本控制相关日志
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`。如需某一特定流程(如多站点版本同步、生命周期与版本控制交互)的详细解析,可继续展开。*

相关推荐
Z思学17 分钟前
Nest 第一步 · 第 3 篇:理解 Controller / Service / Module 三层架构
后端
学长毕业设计18 分钟前
基于SpringBoot的校园爱心志愿管理系统(源码+文档+讲解视频)
vue.js·spring boot·后端
IT_陈寒1 小时前
SpringBoot自动配置失效?这个隐式依赖坑了我三天
前端·人工智能·后端
码视野1 小时前
基于 Spring Boot + Vue3 的【城市雨污水管网液位淤积溯源与立交桥下穿隧洞防汛排涝智控中台】设计与实现(含PRD/三端高保真源码/大屏)
java·前端·人工智能·spring boot·后端
小满zs1 小时前
Go语言第十章(指针)
后端·google·go
十正1 小时前
用 HTTP 条件请求把“强一致“和“低成本
网络·后端·http
2601_962122971 小时前
Spring Cloud——路由网关Zuul
后端·spring·spring cloud
卷无止境1 小时前
从一杯咖啡的订单说起,数据库设计到底是怎么长出来的
后端·python
再吃一根胡萝卜2 小时前
Rust 桌面宠物拖拽踩坑实录:重影、不跟手、置顶失效
后端