对象存储(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)的代码架构整理,后续版本引入了更多优化和抽象,但核心流程保持不变。