OSD.cc 整体业务流程和设计原理详细分析

OSD.cc 整体业务流程和设计原理详细分析

一、整体架构设计

1.1 核心组件架构

OSD.cc 实现了 Ceph 分布式存储系统的核心组件 OSD(Object Storage Daemon),采用分层架构设计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌─────────────────────────────────────────────────────────┐
│ OSD 主类 (OSD) │
│ - 生命周期管理 (init/shutdown) │
│ - 消息分发与路由 │
│ - PG 管理与调度 │
│ - 心跳与健康检查 │
└─────────────────────────────────────────────────────────┘

┌───────────────┼───────────────┐
│ │ │
┌───────▼──────┐ ┌──────▼──────┐ ┌─────▼──────┐
│ OSDService │ │ ObjectStore │ │ Messenger │
│ - PG 调度 │ │ - 数据持久化 │ │ - 消息传递 │
│ - 恢复管理 │ │ - 元数据管理 │ │ - 连接管理 │
│ - Scrub 调度 │ │ │ │ │
│ - 状态管理 │ │ │ │ │
└─────────────┘ └──────────────┘ └────────────┘
│ │ │
└───────────────┼───────────────┘

┌───────▼───────┐
│ PG (Placement │
│ Group) │
│ - 数据一致性 │
│ - Peering │
│ - 恢复/回填 │
└───────────────┘

1.2 设计原则

  1. 分层解耦:OSD 作为协调层,具体操作下沉到 PG、PGBackend 等组件
  2. 异步非阻塞:大量使用异步消息处理和回调机制
  3. 状态机驱动:PG 的 peering、恢复等通过状态机管理
  4. 资源池化:使用线程池、连接池等提高性能
  5. 容错设计:心跳检测、故障报告、自动恢复

二、主要业务流程

2.1 OSD 启动流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pre_init()

init()
├─> 挂载 ObjectStore
├─> 读取 superblock
├─> 验证兼容性特性
├─> 加载 OSDMap
├─> 初始化服务组件
│ ├─> OSDService::init()
│ ├─> 启动心跳线程
│ ├─> 启动 agent 线程
│ └─> 初始化调度器
├─> 加载 PG (load_pgs)
└─> final_init()
├─> 启动 tick 定时器
├─> 启动操作处理线程池
└─> 开始 boot 流程
├─> _preboot()
├─> _get_purged_snaps()
├─> start_waiting_for_healthy()
└─> _send_boot()
└─> 等待 Monitor 响应

关键代码位置:

  • OSD::init() (4051行):主初始化函数
  • OSD::final_init() (4463行):最终初始化
  • OSD::start_boot() (7236行):启动引导流程

2.2 OSDMap 更新流程

OSDMap 是集群拓扑的核心,其更新流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
handle_osd_map(MOSDMap *m)

1. 等待 PG 消费完旧 map(防止缓存溢出)

2. 验证消息来源和 FSID

3. 处理完整 map 和增量 map
├─> 解码 map 数据
├─> 应用增量更新
├─> 验证 CRC
└─> 存储到 ObjectStore

4. 更新 superblock
├─> 记录 epoch 范围
├─> 更新 pg_num_history
└─> 记录 purged_snaps

5. _committed_osd_maps()
├─> 更新各 shard 的 map
├─> 通知 PG map 变更
└─> 触发 PG 处理

6. consume_map() / activate_map()
├─> PG 分裂/合并检测
├─> PG 创建/删除
└─> 推进 PG 状态

关键代码位置:

  • OSD::handle_osd_map() (8561行):处理 OSDMap 消息
  • OSD::_committed_osd_maps() (8968行):提交 map 变更
  • OSD::consume_map() (9664行):PG 消费 map
  • OSD::activate_map() (9724行):激活新 map

2.3 客户端操作处理流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
客户端发送 MOSDOp

ms_fast_dispatch()
├─> 创建 OpRequest
├─> 路由到 PG
│ ├─> 新客户端:等待 map 更新
│ └─> 旧客户端:直接路由
└─> enqueue_op()

op_shardedwq (分片工作队列)

_process() (工作线程)
├─> _lookup_lock_pg()
├─> pg->do_request()
│ ├─> 权限检查
│ ├─> 路由到主/副本
│ ├─> 执行操作
│ └─> 发送回复
└─> pg->unlock()

关键代码位置:

  • OSD::ms_fast_dispatch() (8045行):快速消息分发
  • OSD::enqueue_op():操作入队
  • OSD::_process():操作处理(在 OSD.h 中定义)

2.4 PG 生命周期管理

2.4.1 PG 创建流程

1
2
3
4
5
6
7
8
9
10
11
12
13
handle_fast_pg_create(MOSDPGCreate2 *m)

1. 验证消息来源和权限

2. 检查 PG 是否已存在

3. 创建 PG 实例
├─> PrimaryLogPG::create()
├─> 初始化 PG 状态
└─> 注册到 OSD

4. 触发 Peering
└─> PG::handle_peering_event()

2.4.2 PG 分裂流程

1
2
3
4
5
6
7
8
9
10
consume_map() / activate_map()

track_pools_and_pg_num_changes()
├─> 检测 pg_num 变化
└─> identify_splits_and_merges()

split_pgs()
├─> 创建子 PG
├─> 复制父 PG 数据
└─> 删除父 PG

关键代码位置:

  • OSD::handle_fast_pg_create() (9817行):处理 PG 创建
  • OSD::split_pgs() (9781行):PG 分裂
  • OSDService::identify_splits_and_merges() (359行):识别分裂/合并

2.5 恢复与回填流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
恢复触发
├─> PG Peering 完成
├─> OSDMap 变更
└─> 手动触发

queue_for_recovery()
├─> 计算恢复成本
└─> 加入调度队列

PGRecovery 处理
├─> 预留恢复槽位
├─> 选择恢复对象
├─> 执行恢复操作
└─> 更新 PG 状态

关键代码位置:

  • OSDService::queue_recovery_context() (1828行):恢复上下文入队
  • OSDService::_queue_for_recovery() (2158行):恢复任务入队

2.6 Scrub 流程

1
2
3
4
5
6
7
8
9
10
11
定时触发 / 手动触发

queue_for_scrub()
├─> 创建 scrub 事件消息
└─> 加入调度队列

PGScrub 处理
├─> 检查对象完整性
├─> 比较主副本数据
├─> 修复不一致
└─> 更新统计信息

关键代码位置:

  • OSDService::queue_for_scrub() (2018行):scrub 入队
  • OSD::handle_fast_scrub() (8227行):处理 scrub 消息

2.7 心跳与故障检测流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
heartbeat_entry() (心跳线程)

heartbeat()
├─> 更新统计信息
├─> 检查满载状态
├─> 向对等节点发送 PING
└─> 检查超时

handle_osd_ping()
├─> 处理 PING/PING_REPLY
├─> 更新时间戳
└─> 共享 OSDMap

heartbeat_check()
├─> 检查对等节点响应
└─> 标记故障节点

send_failures()
└─> 向 Monitor 报告故障

关键代码位置:

  • OSD::heartbeat_entry() (6532行):心跳线程入口
  • OSD::heartbeat() (6604行):执行心跳
  • OSD::handle_osd_ping() (6184行):处理心跳消息
  • OSD::heartbeat_check() (6555行):检查心跳超时

三、关键设计原理

3.1 消息分发机制

OSD 采用两级消息分发:

  1. 快速路径 (Fast Dispatch)

    • 无需 OSDMap 锁的消息直接处理
    • 包括:PING、PG 创建/通知、Scrub 等
    • 函数:ms_fast_dispatch()
  2. 标准路径 (Standard Dispatch)

    • 需要 OSDMap 锁的消息
    • 包括:OSDMap 更新、命令等
    • 函数:_dispatch()

设计优势:

  • 减少锁竞争
  • 提高并发性能
  • 降低延迟

3.2 PG 分片架构

OSD 使用分片(Shard)机制管理 PG:

1
2
3
4
OSD
├─> Shard[0] -> PG[0, N, 2N, ...]
├─> Shard[1] -> PG[1, N+1, 2N+1, ...]
└─> Shard[N-1] -> PG[N-1, 2N-1, ...]

设计优势:

  • 减少锁竞争(每个 shard 独立锁)
  • 提高并行度
  • 更好的 NUMA 亲和性

3.3 操作调度机制

OSD 使用多种调度器:

  1. mClock Scheduler(默认):

    • 基于多级反馈队列
    • 支持 QoS 保证
    • 动态成本计算
  2. WeightedPriorityQueue(传统):

    • 基于优先级和权重
    • 固定成本模型

调度队列类型:

  • op_shardedwq:客户端操作队列
  • 恢复队列:queue_recovery_context()
  • Scrub 队列:queue_for_scrub()
  • 快照修剪队列:queue_for_snap_trim()

3.4 OSDMap 缓存与引用计数

OSDMap 采用引用计数管理:

1
2
3
4
5
6
7
8
9
get_nextmap_reserved()
├─> 增加引用计数
└─> 返回 map 引用

PG 使用 map

release_map()
├─> 减少引用计数
└─> 引用为 0 时可释放

设计优势:

  • 防止 map 在使用中被释放
  • 支持 map 缓存
  • 自动内存管理

3.5 满载状态管理

OSD 维护多级满载状态:

1
NONE < NEARFULL < BACKFILLFULL < FULL < FAILSAFE

状态计算:

  • recalc_full_state():根据使用率计算状态
  • check_full_status():更新当前状态
  • need_fullness_update():检查是否需要上报

设计优势:

  • 渐进式节流
  • 防止数据丢失
  • 支持测试注入

3.6 心跳机制设计

心跳采用双通道设计:

1
2
3
4
前端通道 (Front)         后端通道 (Back)
│ │
├─> 客户端网络 ├─> 集群网络
└─> 可配置 └─> 可配置

心跳消息类型:

  • PING:发送心跳请求
  • PING_REPLY:心跳响应
  • YOU_DIED:通知对方已下线

时间戳同步:

  • 使用单调时钟避免时钟漂移
  • HeartbeatStamps 维护时间戳
  • 计算网络延迟和时钟偏差

3.7 故障检测与恢复

故障检测:

  1. 心跳超时检测
  2. 连接断开检测
  3. 操作失败检测

故障处理:

  1. 标记故障节点
  2. 向 Monitor 报告
  3. 触发 PG Peering
  4. 启动恢复流程

四、关键数据结构

4.1 OSD 核心数据结构

1
2
3
4
5
6
7
8
9
class OSD {
OSDService service; // 服务层
ObjectStore *store; // 存储后端
OSDMapRef osdmap; // 当前 OSDMap
map<spg_t, PGRef> pg_map; // PG 映射表
OpShardedWQ op_shardedwq; // 操作队列
HeartbeatThread heartbeat_thread; // 心跳线程
// ...
};

4.2 OSDService 核心数据结构

1
2
3
4
5
6
7
8
class OSDService {
Objecter *objecter; // 对象器
OpScheduler op_scheduler; // 操作调度器
map<epoch_t, OSDMapRef> map_cache; // Map 缓存
RecoveryReserver recovery_reserver; // 恢复资源预留
ScrubReserver scrub_reserver; // Scrub 资源预留
// ...
};

五、性能优化设计

5.1 异步处理

  • 大量使用异步回调和 Future
  • 避免阻塞主线程
  • 提高并发性能

5.2 批处理优化

  • OSDMap 批量更新
  • 操作批量提交
  • 减少系统调用

5.3 NUMA 优化

  • set_numa_affinity():设置 CPU 亲和性
  • 根据存储和网络 NUMA 节点优化
  • 减少跨节点访问

5.4 缓存策略

  • OSDMap 缓存(LRU)
  • 连接缓存
  • 对象元数据缓存

六、容错与可靠性

6.1 数据一致性

  • PG Peering 保证一致性
  • Scrub 检测和修复不一致
  • 事务保证原子性

6.2 故障恢复

  • 自动故障检测
  • 自动恢复和回填
  • 降级模式支持

6.3 优雅关闭

  • shutdown():完整关闭流程
  • fast_shutdown():快速关闭模式
  • 确保数据持久化

七、总结

OSD.cc 是 Ceph 存储系统的核心实现,采用:

  1. 分层架构:清晰的职责划分
  2. 异步设计:高并发性能
  3. 状态机驱动:可靠的状态管理
  4. 资源池化:高效的资源利用
  5. 容错设计:高可用性保障

整个设计体现了分布式系统的最佳实践,在性能、可靠性和可维护性之间取得了良好的平衡。

文章互动

阅读 --

留言

0 条留言

正在加载留言…