对象存储(RGW)架构详解

对象存储(RGW)架构详解 --- 面试版

本文详细解析早期 Ceph 对象存储网关(RGW)的代码架构,涵盖从请求入口到数据落地的每一层处理流程,以及面试高频考点。


一、整体架构图

scss 复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                              客户端层                                        │
│        S3 API (boto3, awscli, mc)  /  Swift API  /  Admin API              │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│  1. 前端层 (Frontend)                                                        │
│  ┌─────────────────────────────────────────────────────────────────────────┐ │
│  │  CivetWeb (早期默认) / Beast (Nautilus+ 默认) / FastCGI (已废弃)       │ │
│  │                                                                         │ │
│  │  入口文件:                                                               │ │
│  │    src/rgw/rgw_civetweb.cc  (CivetWeb 适配层)                           │ │
│  │    src/rgw/rgw_asio_frontend.cc (Beast 适配层)                          │ │
│  │                                                                         │ │
│  │  核心函数:                                                               │ │
│  │    process_request() → 所有请求的入口                                    │ │
│  └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│  2. 请求分发层 (REST Router)                                                 │
│  ┌─────────────────────────────────────────────────────────────────────────┐ │
│  │  src/rgw/rgw_rest.cc                                                   │ │
│  │                                                                         │ │
│  │  核心类: RGWRESTMgr, RGWREST                                           │ │
│  │  核心函数:                                                               │ │
│  │    RGWREST::get_handler() → 根据 URI 匹配 handler                       │ │
│  │    RGWREST::preprocess() → 解析 HTTP 方法、资源类型                     │ │
│  └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│  3. 认证授权层 (Auth)                                                        │
│  ┌─────────────────────────────────────────────────────────────────────────┐ │
│  │  src/rgw/rgw_auth.cc / rgw_auth_s3.cc / rgw_swift_auth.cc              │ │
│  │                                                                         │ │
│  │  核心类: RGWAuthEngine, RGWAuthS3, RGWAuthSwift                        │ │
│  │  核心函数:                                                               │ │
│  │    RGWOp::verify_permission() → 权限校验入口                            │ │
│  │    rgw_build_bucket_policies() → 构建策略上下文                         │ │
│  │    do_aws4_auth_completion() → AWS Signature V4 验证                    │ │
│  └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│  4. 操作层 (Op) - 业务逻辑处理                                               │
│  ┌─────────────────────────────────────────────────────────────────────────┐ │
│  │  src/rgw/rgw_op.cc / rgw_op.h                                          │ │
│  │                                                                         │ │
│  │  核心类: RGWOp (基类)                                                  │ │
│  │    ├── RGWGetObj                                                       │ │
│  │    ├── RGWPutObj                                                       │ │
│  │    ├── RGWDeleteObj                                                    │ │
│  │    ├── RGWListBucket                                                   │ │
│  │    ├── RGWCopyObj                                                      │ │
│  │    ├── RGWInitMultipart                                                │ │
│  │    ├── RGWCompleteMultipart                                            │ │
│  │    └── ... (30+ 个子类)                                               │ │
│  │                                                                         │ │
│  │  生命周期: init() → verify_permission() → pre_exec() → execute()       │ │
│  │            → complete() → send_response()                              │ │
│  └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│  5. 存储抽象层 (Store / SAL)                                                 │
│  ┌─────────────────────────────────────────────────────────────────────────┐ │
│  │  早期: src/rgw/rgw_rados.cc (直接调用 librados)                         │ │
│  │  后期: src/rgw/rgw_sal.cc (存储抽象层)                                  │ │
│  │                                                                         │ │
│  │  核心类: RGWStore (早期) / RGWRadosStore (SAL)                         │ │
│  │  核心函数:                                                               │ │
│  │    get_obj() → 读取对象数据                                             │ │
│  │    put_obj() → 写入对象数据                                             │ │
│  │    delete_obj() → 删除对象                                              │ │
│  │    list_objects() → 列出 bucket 内对象                                  │ │
│  └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│  6. RADOS 集群层                                                             │
│  ┌─────────────────────────────────────────────────────────────────────────┐ │
│  │  src/librados/ (客户端库)                                               │ │
│  │  src/osd/ (OSD 守护进程)                                                │ │
│  │  src/mon/ (MON 守护进程)                                                │ │
│  │                                                                         │ │
│  │  核心类: librados::Rados, librados::IoCtx                              │ │
│  │  核心函数:                                                               │ │
│  │    ioctx.read() → 读取 RADOS 对象                                       │ │
│  │    ioctx.write() → 写入 RADOS 对象                                      │ │
│  │    ioctx.omap_get_vals() → 读取 OMAP (bucket index)                     │ │
│  │    ioctx.omap_set() → 写入 OMAP                                         │ │
│  └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘

二、每层代码处理流程详解


第 1 层:前端层 (Frontend)

1.1 CivetWeb 前端(早期默认)

文件: src/rgw/rgw_civetweb.cc / src/rgw/rgw_civetweb_frontend.cc

cpp 复制代码
// 核心入口函数
int RGWCivetWebFrontend::process_request(struct mg_connection *conn)
{
    // 1. 创建 RGWEnv(环境变量封装)
    RGWEnv env;
    env.init(cio);  // 从 CivetWeb 连接中提取 HTTP header

    // 2. 创建 RGWRequest
    RGWRequest req;

    // 3. 调用核心处理函数
    int ret = process_request(store, &req, env, ...);

    // 4. 返回 HTTP 响应
    return ret;
}

CivetWeb 回调注册:

cpp 复制代码
// 在初始化时注册回调
static int civetweb_callback(struct mg_connection *conn)
{
    return frontend->process_request(conn);
}

// CivetWeb 配置
mg_start({
    "listening_ports", "8080",
    "num_threads", "100",        // 工作线程数
    "request_timeout_ms", "30000",
    nullptr
});
1.2 Beast 前端(Nautilus+ 默认)

文件: src/rgw/rgw_asio_frontend.cc

cpp 复制代码
// 基于 Boost.Asio 的异步模型
class RGWAsioFrontend {
    boost::asio::io_context io_context;
    tcp::acceptor acceptor;

    void accept() {
        auto socket = std::make_shared<tcp::socket>(io_context);
        acceptor.async_accept(*socket, [this, socket](boost::system::error_code ec) {
            if (!ec) {
                handle_connection(socket);  // 处理连接
            }
            accept();  // 继续接受新连接
        });
    }

    void handle_connection(std::shared_ptr<tcp::socket> socket) {
        // 解析 HTTP 请求
        http::async_read(*socket, buffer, request,
            [this, socket](boost::system::error_code ec, size_t bytes) {
                if (!ec) {
                    process_request(request, socket);  // 调用核心处理
                }
            });
    }
};

面试考点:

  • CivetWeb vs Beast 的区别:CivetWeb 是同步多线程模型,每个连接一个线程;Beast 是异步事件驱动,单线程/多线程 io_context
  • 为什么从 CivetWeb 切换到 Beast:性能更好(C10K 问题)、内存占用更低、连接管理更精细

第 2 层:请求分发层 (REST Router)

文件: src/rgw/rgw_rest.cc

cpp 复制代码
// 核心函数:根据 URI 和资源类型匹配 handler
int RGWREST::get_handler(RGWRados *store,
                         struct req_state *s,
                         RGWClientIO *cio,
                         const char *url,
                         const char *method)
{
    // 1. 解析 URI,提取 bucket 和 object 名
    //    例如: /mybucket/myobject → bucket="mybucket", object="myobject"

    // 2. 根据 HTTP 方法选择 Op 类
    //    GET + object → RGWGetObj
    //    PUT + object → RGWPutObj
    //    DELETE + object → RGWDeleteObj
    //    GET + bucket (no object) → RGWListBucket

    // 3. 创建对应的 RGWOp 实例
    RGWOp *op = nullptr;
    if (strcmp(method, "GET") == 0) {
        if (s->object.empty()) {
            op = new RGWListBucket;  // LIST 请求
        } else {
            op = new RGWGetObj;      // GET 对象
        }
    } else if (strcmp(method, "PUT") == 0) {
        op = new RGWPutObj;
    }

    // 4. 设置 Op 的 req_state
    op->init(store, s, cio);

    return 0;
}

URI 解析逻辑:

cpp 复制代码
// 伪代码,实际更复杂
void RGWREST::preprocess(struct req_state *s, const char *url)
{
    // 1. 去除 leading slash
    // 2. 按 '/' 分割
    // 3. 第一部分是 bucket(或空表示 list buckets)
    // 4. 第二部分是 object key(可能包含更多 '/')

    // 特殊处理:
    // - /admin/ → Admin API
    // - ?uploads → Multipart 相关
    // - ?versioning → 版本控制
}

面试考点:

  • RGW 如何解析 S3 URI :虚拟主机风格(bucket.s3.example.com)vs 路径风格(s3.example.com/bucket/object
  • 如何支持子资源?acl, ?policy, ?uploadId 等查询参数的处理

第 3 层:认证授权层 (Auth)

文件: src/rgw/rgw_auth.cc / src/rgw/rgw_auth_s3.cc

3.1 S3 Signature V2 验证流程
cpp 复制代码
// 文件: src/rgw/rgw_auth_s3.cc
int RGW_Auth_S3::authorize(RGWRados *store, struct req_state *s)
{
    // 1. 从 HTTP Header 提取 Authorization
    //    Authorization: AWS AKIAIOSFODNN7EXAMPLE:frJIUN8DYpKDtOLCwo...

    // 2. 解析出 Access Key 和 Signature
    string auth_str = s->env->get("HTTP_AUTHORIZATION", "");
    string access_key, signature;
    parse_auth_header(auth_str, access_key, signature);

    // 3. 根据 Access Key 查询用户数据库(OMAP)
    RGWUserInfo user_info;
    int ret = rgw_get_user_info_by_access_key(store, access_key, user_info);
    if (ret < 0) return -ERR_INVALID_ACCESS_KEY;

    // 4. 使用 Secret Key 重新计算 Signature
    string string_to_sign = build_string_to_sign(s);  // 构造待签名字符串
    string expected_sig = calc_signature(user_info.secret_key, string_to_sign);

    // 5. 比对 Signature
    if (signature != expected_sig) {
        return -ERR_SIGNATURE_NO_MATCH;
    }

    // 6. 设置用户身份
    s->user = user_info;

    return 0;
}
3.2 S3 Signature V4 验证流程
cpp 复制代码
// 文件: src/rgw/rgw_auth_s3.cc
int RGW_Auth_S3_V4::authorize(RGWRados *store, struct req_state *s)
{
    // 1. 解析 Authorization header
    //    Authorization: AWS4-HMAC-SHA256 
    //    Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,
    //    SignedHeaders=host;x-amz-content-sha256;x-amz-date,
    //    Signature=...

    // 2. 提取 Credential 组件
    //    AccessKey / Date / Region / Service / RequestType

    // 3. 构造 Canonical Request
    string canonical_request = build_canonical_request(s);

    // 4. 构造 String to Sign
    //    "AWS4-HMAC-SHA256\n" + Date + "\n" + Scope + "\n" + HexHash(CanonicalRequest)

    // 5. 计算 Signing Key
    //    kSecret = "AWS4" + SecretKey
    //    kDate = HMAC(kSecret, Date)
    //    kRegion = HMAC(kDate, Region)
    //    kService = HMAC(kRegion, Service)
    //    kSigning = HMAC(kService, "aws4_request")

    // 6. 计算 Signature = HMAC(kSigning, StringToSign)

    // 7. 比对并验证
    return verify_signature(store, s, expected_sig);
}
3.3 权限评估流程
cpp 复制代码
// 文件: src/rgw/rgw_op.cc
int RGWOp::verify_permission()
{
    // 1. 构建策略上下文
    rgw_build_bucket_policies(store, s);

    // 2. 评估权限(按优先级)
    //    a. Bucket Policy(最优先)
    //    b. ACL(Access Control List)
    //    c. IAM Policy(如果启用)

    // 3. 检查具体操作权限
    if (s->op == OP_GET) {
        if (!s->bucket_policy.allow_get(s->user)) {
            return -EACCES;
        }
    }

    return 0;
}

面试考点:

  • S3 Signature V2 vs V4 的区别:V2 使用 HMAC-SHA1,V4 使用 HMAC-SHA256;V4 增加了 scope(日期、区域、服务)和 body hash
  • RGW 如何存储用户凭证 :存储在 .users.users.email 等 RADOS 对象的 OMAP 中
  • Bucket Policy vs ACL 的区别:Policy 是 JSON 格式的资源策略,ACL 是传统的读写权限列表

第 4 层:操作层 (Op) - 业务逻辑

文件: src/rgw/rgw_op.cc / src/rgw/rgw_op.h

4.1 RGWOp 基类生命周期
cpp 复制代码
class RGWOp {
public:
    // 1. 初始化:解析参数
    virtual int init(RGWRados *store, struct req_state *s, RGWClientIO *cio) {
        this->store = store;
        this->s = s;
        this->cio = cio;
        return 0;
    }

    // 2. 权限校验
    virtual int verify_permission() = 0;

    // 3. 执行前预处理(日志、限速等)
    virtual int pre_exec() { return 0; }

    // 4. 核心业务逻辑
    virtual void execute() = 0;

    // 5. 完成处理(构造响应)
    virtual void complete() {
        send_response();
    }

    // 6. 发送 HTTP 响应
    virtual void send_response() = 0;
};
4.2 PUT Object 详细流程
cpp 复制代码
// 文件: src/rgw/rgw_op.cc
class RGWPutObj : public RGWOp {
    bufferlist data;           // 数据缓冲区
    RGWObjManifest manifest;   // 对象分片映射
    string etag;               // MD5 校验值

public:
    int init() override {
        // 1. 解析 Content-Length
        s->content_length = parse_content_length(s->env);

        // 2. 解析自定义元数据 (x-amz-meta-*)
        parse_metadata(s->env, attrs);

        // 3. 解析 ACL(如果提供)
        if (s->env->exists("HTTP_X_AMZ_ACL")) {
            parse_acl(s->env->get("HTTP_X_AMZ_ACL"));
        }

        return 0;
    }

    void execute() override {
        // 1. 创建对象处理器
        RGWPutObjProcessor processor(store, s->bucket, s->object);

        // 2. 准备写入(创建 RADOS 对象)
        processor.prepare();

        // 3. 循环读取请求体并写入
        while (true) {
            // 从客户端读取数据
            int len = read_client_data(data);
            if (len <= 0) break;

            // 计算 MD5
            hash.Update(data.c_str(), len);

            // 写入数据(可能分片)
            processor.handle_data(data);

            // 如果达到分片阈值(默认 4MB),创建新 RADOS 对象
            if (processor.need_new_part()) {
                processor.next_part();
            }
        }

        // 4. 完成写入,获取 ETag
        etag = hash.finalize();

        // 5. 设置对象元数据
        attrs[RGW_ATTR_ETAG] = etag;
        attrs[RGW_ATTR_CONTENT_TYPE] = s->env->get("CONTENT_TYPE");

        // 6. 提交对象(写入 OMAP 元数据)
        processor.complete(etag, attrs);

        // 7. 更新 Bucket Index
        rgw_bucket_update_index(store, s->bucket, s->object, etag, s->content_length);
    }
};
4.3 GET Object 详细流程
cpp 复制代码
// 文件: src/rgw/rgw_op.cc
class RGWGetObj : public RGWOp {
    off_t ofs;      // 读取偏移
    off_t end;      // 读取结束位置

public:
    void execute() override {
        // 1. 从 Bucket Index 获取对象元数据
        RGWObjEnt obj_ent;
        int ret = store->get_obj_state(s->bucket, s->object, obj_ent);
        if (ret < 0) {
            s->err.ret = -ENOENT;
            return;
        }

        // 2. 检查权限
        if (!verify_object_permission(s->bucket, s->object, RGW_PERM_READ)) {
            s->err.ret = -EACCES;
            return;
        }

        // 3. 解析 Range 请求
        parse_range(s->env->get("HTTP_RANGE"), ofs, end);

        // 4. 获取对象 manifest(分片信息)
        RGWObjManifest manifest;
        store->get_obj_manifest(s->bucket, s->object, manifest);

        // 5. 计算需要读取的分片
        vector<RGWObjManifestPart> parts;
        manifest.get_range(ofs, end, parts);

        // 6. 循环读取每个分片并发送给客户端
        for (auto &part : parts) {
            // 从 RADOS 读取
            bufferlist bl;
            store->get_obj_data(part.loc, part.ofs, part.size, bl);

            // 发送给客户端
            send_response_data(bl);
        }
    }
};

面试考点:

  • RGWOp 的生命周期:init → verify_permission → pre_exec → execute → complete → send_response
  • PUT 对象如何分片:默认 4MB 一个 RADOS 对象,通过 RGWObjManifest 记录映射关系
  • Range 请求如何实现:解析 HTTP Range header,计算涉及的分片,按需读取

第 5 层:存储抽象层 (Store / SAL)

5.1 早期实现:直接调用 librados

文件: src/rgw/rgw_rados.cc

cpp 复制代码
class RGWRados {
    librados::Rados rados;      // RADOS 集群连接
    librados::IoCtx io_ctx;     // IO 上下文(对应 pool)

public:
    // 读取对象数据
    int get_obj_data(rgw_obj& obj, off_t ofs, off_t end, bufferlist& bl) {
        // 1. 获取对象的 RADOS 对象名
        string oid = obj.get_oid();

        // 2. 创建读操作
        librados::ObjectReadOperation op;
        op.read(ofs, end - ofs + 1, &bl, nullptr);

        // 3. 执行读
        return io_ctx.operate(oid, &op, nullptr);
    }

    // 写入对象数据
    int put_obj_data(rgw_obj& obj, bufferlist& bl, off_t ofs) {
        librados::ObjectWriteOperation op;
        op.write(ofs, bl);
        return io_ctx.operate(oid, &op);
    }

    // 读取对象元数据(OMAP)
    int get_obj_attrs(rgw_obj& obj, map<string, bufferlist>& attrs) {
        librados::ObjectReadOperation op;
        op.getxattrs(&attrs, nullptr);
        return io_ctx.operate(oid, &op, nullptr);
    }

    // 写入对象元数据(OMAP)
    int set_obj_attrs(rgw_obj& obj, map<string, bufferlist>& attrs) {
        librados::ObjectWriteOperation op;
        for (auto &attr : attrs) {
            op.setxattr(attr.first, attr.second);
        }
        return io_ctx.operate(oid, &op);
    }
};
5.2 后期演进:SAL(Storage Abstraction Layer)

文件: src/rgw/rgw_sal.cc / src/rgw/rgw_sal_rados.cc

cpp 复制代码
// 抽象接口
class RGWStore {
public:
    virtual int get_object(const RGWBucketInfo& bucket, 
                           const rgw_obj_key& key,
                           RGWObject **obj) = 0;
    virtual int put_object(RGWObject *obj, bufferlist& data) = 0;
    virtual int delete_object(RGWObject *obj) = 0;
    virtual int list_objects(const RGWBucketInfo& bucket,
                             const string& prefix,
                             vector<RGWObjEnt>& result) = 0;
};

// RADOS 实现
class RGWRadosStore : public RGWStore {
    RGWRados *rados;  // 底层仍然使用 RGWRados

public:
    int get_object(...) override {
        return rados->get_obj(...);
    }
    // ...
};

// DBStore 实现(实验性,用于测试)
class RGWDBStore : public RGWStore {
    // 使用 SQLite/Postgres 存储
};

面试考点:

  • 为什么引入 SAL:解耦 RGW 与底层存储,支持多种后端(RADOS、DB、POSIX)
  • RGW 与 librados 的关系 :RGW 是 librados 的客户端,通过 IoCtx 进行对象操作
  • OMAP 是什么:RADOS 对象的扩展属性存储,用于 bucket index 和对象元数据

第 6 层:Bucket Index 实现

文件: src/rgw/rgw_bucket.cc / src/rgw/rgw_rados.cc

6.1 单 Shard 实现(早期)
cpp 复制代码
// Bucket Index 存储在一个 RADOS 对象上
// 对象名: .dir.<bucket_marker>

int rgw_bucket_update_index(RGWRados *store, 
                            const string& bucket_name,
                            const string& obj_name,
                            const string& etag,
                            uint64_t size)
{
    // 1. 构造 bucket index 对象名
    string index_oid = ".dir." + bucket_marker;

    // 2. 构造对象条目
    RGWObjEnt entry;
    entry.name = obj_name;
    entry.etag = etag;
    entry.size = size;
    entry.mtime = ceph_clock_now();

    // 3. 序列化为 bufferlist
    bufferlist bl;
    encode(entry, bl);

    // 4. 写入 OMAP
    librados::ObjectWriteOperation op;
    op.omap_set(obj_name, bl);  // key=对象名, value=序列化后的元数据

    return io_ctx.operate(index_oid, &op);
}
6.2 多 Shard 实现
cpp 复制代码
// 对象名: .dir.<bucket_marker>.<shard_id>

int rgw_bucket_list_objects(RGWRados *store,
                            const string& bucket_name,
                            const string& prefix,
                            vector<RGWObjEnt>& result)
{
    // 1. 获取 bucket 的 shard 数
    int num_shards = bucket_info.num_shards;

    // 2. 计算对象所属的 shard
    int shard_id = rgw_bucket_shard_index(obj_name, num_shards);

    // 3. 构造 index 对象名
    string index_oid = ".dir." + bucket_marker + "." + to_string(shard_id);

    // 4. 从 OMAP 读取
    map<string, bufferlist> entries;
    librados::ObjectReadOperation op;
    op.omap_get_vals(prefix, 1000, &entries, nullptr);

    int ret = io_ctx.operate(index_oid, &op, nullptr);

    // 5. 反序列化
    for (auto &e : entries) {
        RGWObjEnt entry;
        bufferlist::iterator iter = e.second.begin();
        decode(entry, iter);
        result.push_back(entry);
    }

    return ret;
}

面试考点:

  • Bucket Index 存储在哪里 :RADOS 对象的 OMAP 中,对象名为 .dir.<marker>[.<shard_id>]
  • 为什么要分 Shard:单 shard 对象数过多(>10万)时,LIST 操作需要遍历大量 OMAP,性能下降
  • Dynamic Resharding 如何实现:创建新 index(更多 shard),后台迁移对象元数据,原子切换指针

三、Multipart Upload 详细流程

文件: src/rgw/rgw_op.cc (RGWInitMultipart, RGWUploadPart, RGWCompleteMultipart)

dart 复制代码
客户端                              RGW
  │                                  │
  │  POST /bucket/obj?uploads       │
  │─────────────────────────────────►│
  │                                  │  1. RGWInitMultipart::execute()
  │                                  │     - 生成 uploadId (UUID)
  │                                  │     - 创建 .meta 对象存储 part 信息
  │                                  │
  │  200 OK + UploadId              │
  │◄─────────────────────────────────│
  │                                  │
  │  PUT /bucket/obj?partNumber=1&uploadId=xxx
  │─────────────────────────────────►│
  │                                  │  2. RGWUploadPart::execute()
  │                                  │     - 写入 part 数据到临时对象
  │                                  │     - 更新 .meta 对象记录 part 信息
  │                                  │
  │  200 OK + ETag                  │
  │◄─────────────────────────────────│
  │         ... (重复上传 parts)      │
  │                                  │
  │  POST /bucket/obj?uploadId=xxx  │
  │  <CompleteMultipartUpload>      │
  │─────────────────────────────────►│
  │                                  │  3. RGWCompleteMultipart::execute()
  │                                  │     - 读取 .meta 获取所有 part 信息
  │                                  │     - 合并 part 对象(或创建 manifest)
  │                                  │     - 更新 bucket index
  │                                  │     - 删除 .meta 和临时 part 对象
  │                                  │
  │  200 OK                         │
  │◄─────────────────────────────────│

关键数据结构:

cpp 复制代码
// 存储在 .meta 对象中的 part 信息
struct RGWUploadPartInfo {
    uint32_t num;           // part number
    uint64_t size;          // part 大小
    string etag;            // part 的 ETag
    ceph::real_time modified; // 修改时间
};

// 完整的 multipart 元数据
struct RGWMultipartMeta {
    string upload_id;
    string bucket;
    string key;
    map<uint32_t, RGWUploadPartInfo> parts;
};

面试考点:

  • Multipart 的元数据存在哪里 :存储在 .meta 的 RADOS 对象中(OMAP)
  • Complete 时是否真正合并数据:早期版本会物理合并,后期版本使用 RGWObjManifest 逻辑引用
  • Abort Multipart 会怎样:删除 .meta 对象和已上传的 part 对象,触发 GC

四、面试高频问题汇总

4.1 架构设计类

问题 答案要点
RGW 的整体架构是什么? 前端(Beast/CivetWeb) → REST路由 → 认证授权 → Op业务逻辑 → Store存储层 → RADOS集群
为什么从 CivetWeb 切换到 Beast? 性能(异步事件驱动 vs 同步多线程)、内存占用、连接管理
什么是 SAL?为什么引入? Storage Abstraction Layer,解耦 RGW 与底层存储,支持多种后端
RGW 与 RADOS 的关系? RGW 是 RADOS 的客户端,通过 librados 进行对象操作

4.2 数据流类

问题 答案要点
PUT Object 的完整流程? init → 权限校验 → 循环读取请求体 → 分片写入 RADOS → 计算 ETag → 写入元数据(OMAP) → 更新 Bucket Index
GET Object 如何处理 Range 请求? 解析 Range header → 计算涉及的分片 → 按需从 RADOS 读取 → 流式返回
Bucket Index 存储在哪里? RADOS 对象的 OMAP 中,对象名为 .dir.<marker>[.<shard_id>]
为什么要 Dynamic Resharding? 单 shard 对象数过多时 LIST 性能下降,需要动态增加 shard 数

4.3 认证授权类

问题 答案要点
S3 Signature V4 的验证流程? 解析 Credential → 构造 Canonical Request → 构造 String to Sign → 计算 Signing Key → HMAC 计算 Signature → 比对
Bucket Policy vs ACL 的区别? Policy 是 JSON 格式的资源策略(细粒度),ACL 是传统的读写权限列表(粗粒度)
RGW 如何存储用户凭证? 存储在 .users.users.email 等 RADOS 对象的 OMAP 中

4.4 性能优化类

问题 答案要点
RGW 的性能瓶颈在哪里? Bucket Index 的 OMAP 操作(LIST)、大对象的元数据操作、连接数限制
如何优化 LIST 性能? 增加 shard 数、使用 indexless bucket(实验性)、优化 prefix 过滤
如何排查 RGW 内存泄漏? ASAN、valgrind、监控 RSS 增长、检查第三方依赖版本(如 libcurl)

4.5 故障排查类

问题 答案要点
RGW 返回 503 是什么原因? 后端 RADOS 不可用、连接数超限、线程池满
对象删除后空间未释放? GC 异步回收、检查 GC 队列、使用 radosgw-admin gc process
Multipart 上传后对象不存在? 检查是否调用了 CompleteMultipart、检查 .meta 对象是否存在

五、代码阅读建议

文件 内容 面试重点
src/rgw/rgw_main.cc 主入口、进程初始化 启动流程
src/rgw/rgw_civetweb.cc CivetWeb 前端适配 前端架构
src/rgw/rgw_asio_frontend.cc Beast 前端适配 异步模型
src/rgw/rgw_rest.cc REST 路由、请求分发 URI 解析
src/rgw/rgw_auth.cc 认证框架 认证流程
src/rgw/rgw_auth_s3.cc S3 认证实现 Signature V2/V4
src/rgw/rgw_op.cc 所有 Op 的实现 业务逻辑
src/rgw/rgw_op.h Op 基类定义 生命周期
src/rgw/rgw_rados.cc 早期存储层 RADOS 交互
src/rgw/rgw_sal.cc 存储抽象层 SAL 设计
src/rgw/rgw_bucket.cc Bucket 操作 Index 实现
src/rgw/rgw_obj_manifest.cc 对象分片映射 大对象处理

本文基于 Ceph 早期版本(Hammer ~ Nautilus)的代码架构整理,后续版本引入了更多优化和抽象,但核心流程保持不变。

相关推荐
Python私教1 小时前
软件开发报价为什么能差 3 倍:需求范围、验收标准和变更成本怎么算
后端·python·架构
苏三说技术1 小时前
为什么越来越多人用gRPC?
后端
MacroZheng1 小时前
程序员看文档神器,装上它,看Spring官方文档就一目了然了!
java·人工智能·后端
想风1 小时前
Claude Code 实用技巧工作坊 —— Boris Cherny
前端·后端·github
长栎1 小时前
Java 17 的模式匹配,正在杀死访问者模式
后端
风曳丷1 小时前
# 03|恶意文本如何一路摸到工具按钮
后端
2501_915918411 小时前
Rust 程序抓包解密,rustls 不认系统证书的几种办法
开发语言·后端·网络协议·ios·adb·https·rust
深漂的华哥1 小时前
Ruoyi-Vue-Plus(V5.6.2) 开发环境搭建
java·前端·spring boot·后端·spring·ruoyi
小月土星1 小时前
构建 AI 应用的后端,最主流的选择就是 Python + FastAPI。
后端·fastapi