Ceph RGW 对象上传完整流程解析:请求解析 → 执行 → 落盘

Ceph RGW 对象上传完整流程解析:请求解析 → 执行 → 落盘

下面从 一条 PUT /bucket/object 请求进入 RGW 开始,完整梳理对象上传流程,并标注每个阶段在 RGW 源码/架构中体现的设计模式。

1. 全景流程图

sequenceDiagram participant C as Client participant F as HTTP Frontend participant R as RESTMgr/Handler participant A as Auth Engine participant OP as RGWPutObj participant S as rgw::sal::Store / RGWRados participant RADOS as Ceph RADOS C->>F: PUT /bucket/object HTTP/1.1 F->>R: 解析 req_state / 路由 R->>A: 认证、鉴权 A-->>R: user / policy / acl R->>OP: 工厂创建 RGWPutObj OP->>S: put_object / write data S->>RADOS: 写 shadow/head 对象、xattrs、manifest S->>S: 更新 bucket index OP-->>R: 返回 ETag / version_id R-->>F: 构造 HTTP 200 响应 F-->>C: 200 OK

2. 请求接收与解析阶段

RGW 的请求处理可以概括为:接收 → 解析 → 验证 → 路由 → 执行 → 响应 。在架构上,RGWFrontendRGWRESTMgrRGWHandler 是这个阶段的核心组件。

2.1 HTTP Frontend 接收连接

RGW 支持多种前端,例如 Civetweb、Beast 等。前端负责监听端口、读取 socket、解析 HTTP 协议层内容,然后把请求交给 RGW 内部处理逻辑。 对于一次对象上传:

http 复制代码
PUT /mybucket/myobject HTTP/1.1
Host: rgw.example.com
Content-MD5: ...
Content-Type: application/octet-stream
x-amz-meta-owner: alice
Authorization: AWS4-HMAC-SHA256 ...

RGW 会解析:

  • HTTP 方法:PUT
  • URL:bucket=mybucketobject=myobject
  • 公共请求头:如 Content-Type
  • S3 特有头:如 Content-MD5x-amz-meta-*
  • 查询参数:如 uploadIdpartNumber
  • 请求体:大对象通常不会全部读进内存,而是作为流式输入处理

设计模式体现:

这一层常体现 装饰器模式。RGW 会把底层 socket 封装成 I/O 对象,然后叠加缓冲、分块传输编码、HTTP 解析、限速等过滤器能力,而不是把所有逻辑写进一个巨大的 socket 处理类。


3. REST 路由与 Handler 分发

RGW 需要同时支持 S3、Swift、Admin API 等协议风格。RGWRESTMgr 会根据 URL、Authorization 头、路径风格判断使用哪类 Handler,例如:

  • RGWHandlerREST_S3
  • RGWHandlerREST_Swift
  • RGWHandler_Admin 简化后的逻辑如下:
cpp 复制代码
RGWHandler* RGWRESTMgr::get_handler(req_state* s) {
    if (is_s3_request(s)) {
        return new RGWHandlerREST_S3();
    } else if (is_swift_request(s)) {
        return new RGWHandlerREST_Swift();
    }
    return nullptr;
}

设计模式体现:

这里体现了 工厂模式 / 简单工厂。调用方不直接 new 具体协议处理器,而是通过 REST 管理器统一创建。


4. 认证与授权阶段

对于 S3 对象上传,RGW 会解析 Authorization 头,常见包括:

  • AWS Signature V2
  • AWS Signature V4
  • 匿名访问
  • OpenStack Keystone 认证
  • 预签名 URL 简化模型:
cpp 复制代码
class AuthEngine {
public:
    virtual int authenticate(req_state* s) const = 0;
    virtual ~AuthEngine() = default;
};
class AuthV4 : public AuthEngine {
public:
    int authenticate(req_state* s) const override {
        // 校验 AWS4 签名
        return 0;
    }
};
class AuthV2 : public AuthEngine {
public:
    int authenticate(req_state* s) const override {
        // 校验 S3 v2 签名
        return 0;
    }
};
class AuthStrategy {
    std::vector<std::unique_ptr<AuthEngine>> engines;
public:
    int authenticate(req_state* s) const {
        for (const auto& e : engines) {
            if (e->authenticate(s) == 0)
                return 0;
        }
        return -EPERM;
    }
};

认证完成后,RGW 还要检查:

  • 用户是否有 bucket 写权限
  • bucket policy 是否允许
  • object ACL 是否限制写入
  • 是否触发版本控制、对象锁、加密等策略

设计模式体现:

这是典型的 策略模式 。不同认证算法实现同一个接口,RGW 根据请求特征选择或串联策略。

如果多个认证引擎按优先级依次尝试,也带有 责任链模式 的影子。


5. 创建 RGWOp:从 HTTP 请求到业务操作对象

RGW 不会在路由层直接写死"上传对象应该调用哪个函数",而是把每个 REST 操作抽象成一个 RGWOp 对象。 例如对于 S3 PUT 对象,对应的核心类通常是 RGWPutObj。搜索资料中也提到,PUT 上传的核心处理流程最终落在 RGWPutObj::execute() 中。 简化逻辑如下:

cpp 复制代码
RGWOp* RGWHandlerREST_S3::get_obj_op() {
    switch (s->op) {
        case OP_GET:
            return new RGWGetObj_ObjStore_S3();
        case OP_PUT:
            return new RGWPutObj_ObjStore_S3();
        case OP_DELETE:
            return new RGWDeleteObj_ObjStore_S3();
        default:
            return nullptr;
    }
}

随后 Handler 会初始化这个 Op:

cpp 复制代码
RGWOp* op = get_obj_op();
if (!op) {
    return -ERR_METHOD_NOT_ALLOWED;
}
op->init(store, s, this);
op->pre_processing();
op->execute();
op->complete();

设计模式体现:

这里是 RGW 中非常经典的 工厂模式 + 模板方法模式

  • 工厂模式 :由 Handler 根据请求方法创建具体 RGWOp
  • 模板方法模式RGWOp 基类定义了通用生命周期,如初始化、权限校验、执行、完成、错误响应;子类只实现自己的 execute() 细节。

6. 对象上传执行阶段:数据面核心流程

6.1 RGW 对象不是简单等于一个 RADOS 对象

一个 S3/RGW 对象在底层可能对应一个或多个 RADOS 对象。RGW 使用三个关键概念管理映射关系:

概念 含义
rgw_max_chunk_size RGW 下发到 RADOS 的单个 I/O 大小,也决定 head 对象大小
rgw_obj_stripe_size 条带大小,后续数据按此大小切分成多个 RADOS 对象
manifest 描述用户对象与底层 RADOS 对象的映射关系
数据通常写入类似 {zone}.rgw.buckets.data 的存储池中。

7. 普通整体上传流程

假设上传一个对象:

http 复制代码
PUT /bucket/object
Content-Length: 20M

7.1 对象大小小于等于 chunk size

如果对象小于等于 rgw_max_chunk_size,则通常只生成一个 RADOS 对象,即 head 对象。 该对象中包含:

  • 用户数据
  • 对象元数据
  • xattrs
  • manifest OID 形式可以近似理解为:
text 复制代码
{bucket_id}_{object_name}

7.2 对象大小大于 chunk size

如果对象大于 chunk size,则拆分为:

  • head 对象:保存前 chunk 数据和元数据
  • shadow / tail 对象:保存剩余数据 搜索资料中提到,大于 chunk size 的对象会被拆成 head、多个中间对象和 tail 对象。shadow 对象的名字中通常会引入随机串,避免并发上传同名对象时冲突。 简化形式:
text 复制代码
head:   {bucket_id}_{object_name}
shadow: {bucket_id}_shadow_{random}_{stripe_id}

7.3 写入顺序:先 shadow,后 head,最后 index

RGW 上传对象时,典型顺序是:

  1. 写 shadow / tail 对象
  2. 写 head 对象
  3. 更新 bucket index 这个顺序非常关键。资料中分析指出,RGW 会先生成随机串作为 shadow 对象 OID 的一部分,保证多个会话即使上传同名对象,在 shadow 阶段也不会冲突。随后最后写 head,让 head 成为对象可见性的提交点。 伪代码如下:
cpp 复制代码
int RGWRados::put_object(...) {
    RGWObjManifest manifest;
    // 1. 先写非 head 部分
    while (off < data_len) {
        write_shadow_object(...);
        manifest.append_stripe(...);
    }
    // 2. 写 head 对象,附带 xattrs + manifest
    write_head_object(
        head_oid,
        first_chunk_data,
        xattrs,
        manifest
    );
    // 3. 更新 bucket index
    update_bucket_index(...);
}

设计模式体现:

这里的 manifest 是一个典型的 组合/聚合型元数据结构

如果从工程模式角度看,构建 manifest 的过程类似 Builder 思想:逐步追加 stripe、偏移、RADOS 对象信息,最后由 head 对象统一提交。


8. 分段上传流程:Multipart Upload

大文件常用 S3 Multipart Upload,也就是 MPU。它分为三步:

8.1 初始化 MPU

客户端发送:

http 复制代码
POST /bucket/object?uploads

RGW 会生成一个 uploadId,并记录 MPU 元数据。

8.2 上传每个 Part

客户端可以并行上传:

http 复制代码
PUT /bucket/object?partNumber=1&uploadId=xxx
PUT /bucket/object?partNumber=2&uploadId=xxx

每个 Part 在底层会被写成临时 RADOS 对象。若单个 Part 大于条带大小,也会继续拆分。 搜索资料中提到,每个分段的第一个对象通常是 _multipart_...,其余为 _shadow_...

8.3 Complete MPU:原子提交元数据

当所有 Part 上传完成后,客户端发送:

http 复制代码
POST /bucket/object?uploadId=xxx

RGW 不会把已有数据重新拷贝一遍,而是:

  1. 读取所有 Part 信息
  2. 组装完整 manifest
  3. 写最终 head 对象
  4. head 对象的 manifest 指向各个 Part 对应的 RADOS 对象 资料中明确指出,MPU Complete 是一个近似原子的元数据提交操作,最终 head 对象本身可能不包含用户数据,而主要包含 manifest。 简化如下:
cpp 复制代码
int RGWRados::complete_mpu(...) {
    RGWObjManifest manifest;
    for (part : parts) {
        manifest.append(part.stripe_info);
    }
    write_head_object(
        final_head_oid,
        no_data_or_empty_data,
        xattrs,
        manifest
    );
    update_bucket_index(...);
}

设计模式体现:

MPU Complete 很适合用 命令模式 / 事务提交语义 来理解:

  • 每个上传 Part 是独立的子操作;
  • Complete 汇总这些子操作,一次性提交最终可见状态;
  • head 对象是提交点。

9. 元数据、索引与响应阶段

对象数据写完后,RGW 还需要维护控制面状态。

9.1 对象元数据

对象元数据主要包括:

  • Owner
  • ACL
  • ETag
  • Content-Type
  • 用户自定义元数据
  • manifest
  • 版本信息
  • 加密信息
  • 对象锁信息 这些信息通常保存在 head 对象的 xattrs 或 OMAP 中。

9.2 Bucket Index

bucket index 用于支持 LIST objects、bucket 统计、版本管理、生命周期等能力。上传对象完成后,RGW 会更新 bucket index,使该对象可以被列举和检索。 资料中提到,bucket index 是 RGW 对象可列举能力的关键,并且支持动态分片以扩展单个 bucket 的对象数量。

9.3 响应客户端

PUT 成功后,RGW 通常返回:

http 复制代码
HTTP/1.1 200 OK
ETag: "..."
x-amz-version-id: ...

10. 核心对象布局总结

以下表格总结普通上传与分段上传的主要 RADOS 对象形态:

场景 RADOS 对象 作用
小对象整体上传 head 对象 保存数据 + 元数据 + manifest
大对象整体上传 head 对象 保存前 chunk 数据、元数据、manifest
大对象整体上传 shadow 对象 保存后续 stripe 数据
MPU 上传 Part multipart 对象 保存某个 Part 的第一个 stripe
MPU 上传 Part shadow 对象 保存某个 Part 的后续 stripe
MPU Complete final head 对象 保存最终 manifest,指向所有 Part 数据

11. RGW 上传流程中的设计模式全景表

流程节点 设计模式 在 RGW 中的体现
HTTP I/O 处理 装饰器模式 对 socket 叠加缓冲、chunked、HTTP 解析、限速等能力
协议识别与 Handler 创建 工厂模式 / 简单工厂 根据 S3/Swift/Admin 创建对应 Handler
RGWOp 创建 工厂模式 根据 PUT/GET/DELETE 创建 RGWPutObjRGWGetObj
RGWOp 生命周期 模板方法模式 基类定义 init / execute / complete 流程,子类实现细节
认证 策略模式 S3 v2、S3 v4、Keystone、匿名认证实现统一接口
多认证引擎尝试 责任链模式 / 组合策略 依次尝试多个认证方式
访问控制 策略模式 ACL、bucket policy、用户策略统一判断
librados 访问 适配器模式 / 门面模式 RGWRados / rgw::sal::Store 封装底层 librados API
manifest 构建 Builder 思想 / 组合模式 逐步追加 stripe、offset、RADOS 对象信息
MPU Complete 命令模式 / 事务提交语义 汇总所有 Part,一次性提交 head manifest
多 RGW 元数据一致性周边 观察者模式思想 依赖 RADOS watch/notify 或 bilog 等机制同步缓存/日志

12. 一个更完整的简化代码视图

以下代码是架构示意,不是 Ceph 源码逐行还原,但能帮助面试表达:

cpp 复制代码
class RGWOp {
public:
    virtual ~RGWOp() = default;
    void run() {
        init_processing();      // 模板方法固定流程
        verify_permission();
        execute();              // 子类实现
        complete();
    }
protected:
    virtual int init_processing() = 0;
    virtual int verify_permission() = 0;
    virtual int execute() = 0;
    virtual void complete() = 0;
};
class RGWPutObj : public RGWOp {
    rgw::sal::Store* store;
    req_state* s;
protected:
    int init_processing() override {
        // 解析 Content-MD5、Content-Type、x-amz-meta-* 等
        return 0;
    }
    int verify_permission() override {
        // 检查 bucket policy、ACL、用户权限
        return 0;
    }
    int execute() override {
        // 核心上传流程
        return store->put_object(
            s->bucket,
            s->object,
            s->in_stream,
            s->attrs,
            s->manifest
        );
    }
    void complete() override {
        // 响应 ETag、version_id 等
    }
};
class RGWHandlerREST_S3 {
public:
    RGWOp* get_op(req_state* s) {
        switch (s->op) {
            case OP_PUT:
                return new RGWPutObj();   // 工厂模式
            default:
                return nullptr;
        }
    }
};
class RadosStore : public rgw::sal::Store {
    RGWRados rados;
public:
    int put_object(...) override {
        // 适配器/门面:把 RGW 语义转换为 RADOS 语义
        return rados.put(...);
    }
};

13. 面试总结话术

"一条 RGW 对象上传请求,表面上是一个 HTTP PUT,背后会经历请求解析、协议路由、认证授权、RGWOp 创建、数据落盘、元数据和 bucket index 更新几个阶段。

RGW 的核心设计非常清晰:前端负责 HTTP 协议,Handler 负责协议级路由,RGWOp 负责具体业务动作,Store/RGWRados 负责屏蔽底层 RADOS。对象不是简单写成一个 RADOS 对象,而是根据 chunk 和 stripe 切成 head/shadow 等多个 RADOS 对象,并用 manifest 描述映射。

设计模式上,Handler 创建 Op 是工厂模式,RGWOp 生命周期是模板方法,认证是策略模式,librados 封装是适配器/门面,I/O 处理链是装饰器,Multipart Complete 则体现了原子提交和命令式事务语义。"

相关推荐
coderCN1 小时前
HTTP(动静分离)
后端
jj_ccwgw1 小时前
Kafka 集群开机自启动配置指南
后端·kafka
AskHarries1 小时前
外链到底还有没有用
后端
孫治AllenSun1 小时前
【LangChain4J-03】Springboot 项目搭建框架
java·spring boot·后端
吾皇斯巴达2 小时前
O_DIRECT与gcsfuse的go程序中实现内存页面对齐
开发语言·后端·golang
马可家的菠萝3 小时前
收藏不是终点:一个真正有用的个人知识库,至少要完成“收集 → 理解 → 行动”
前端·后端·架构
东小西3 小时前
【SAA实战】第 2 篇:模型与消息——ReactAgent 怎么挑模型、怎么传消息
java·后端·spring
qo_tn3 小时前
Docker_02-容器操作_11-怎样进入容器执行命令
后端
架构精进之路3 小时前
Claude Code 深度使用指南:从"会用"到"用好"的7个进阶心法
后端·openai·ai编程