引言
本系列文章是对 OceanBase v4.3.5_CE_BP6 开源源码的逐模块深度解读。本文解读 observer 进程的启动流程,覆盖 main.cpp 和 ob_server.cpp 两个核心文件。
一、main.cpp — 进程入口与启动编排
1.1 整体结构
main.cpp 是 OceanBase observer 进程的入口文件,整体约 700 行,结构如下:
| 代码区域 | 行范围(约) | 职责 |
|---|---|---|
| 头文件与宏定义 | 1-60 | 引入 share/ob_tenant_id.h、ob_signal_handler.h 等;定义 USING_LOG_PREFIX |
| print_help() / dump_config_to_json() | 65-130 | 命令行帮助与配置导出(-z 参数导出 JSON 格式配置项) |
| parse_opts() | 135-230 | 解析命令行参数:-o 指定配置目录、-l 日志级别、-p PID 文件、-r 重做/恢复模式等 |
| check_uid_before_start() | 235-270 | 安全检查:确保以非 root 用户运行(生产环境不应用 root 启动 observer) |
| print_all_thread() | 275-310 | 调试辅助:在关键节点(启动前/销毁前)打印所有线程列表 |
| inner_main() | 315-650 | 核心启动函数,编排全部初始化流程 |
| main() | 655-700 | 薄包装,调用 inner_main 并处理异常退出 |
1.2 inner_main() — 七阶段启动编排
inner_main() 是整个启动流程的编排核心,按执行顺序分为 7 个阶段:
阶段 1:信号栈与内存基础设置
//【柚子解读】为信号处理函数分配独立的替代信号栈(16MB),
// 这样即使主线程栈溢出,信号处理函数仍能正常执行。
// ASAN 模式下跳过(ASAN 自带机制处理)。
void *ptr = malloc(SIG_STACK_SIZE);
stack_t ss;
ss.ss_sp = ptr;
ss.ss_size = SIG_STACK_SIZE;
ss.ss_flags = 0;
sigaltstack(&ss, nullptr);
同时初始化 trace_id 序列生成器(用当前时间戳作为种子),设置日志文件大小上限(256MB/文件)。
阶段 2:参数解析与安全检查
//【柚子解读】解析命令行参数,关键选项:
// -o 指定配置文件目录(默认 ./etc)
// -l 设置日志级别(ERROR/WARN/INFO/DEBUG/TRACE)
// -p PID 文件路径(防止多实例)
// -r 恢复模式(只做数据恢复,不启动网络服务)
// -z 导出当前所有配置项为 JSON 并退出
// -m 最小模式启动(仅核心模块)
// -A 仲裁模式(Arbitration Service,仅做仲裁不存数据)
opts.log_level_ = OB_LOG_LEVEL_WARN; // 默认 WARN,避免启动日志刷屏
parse_opts(argc, argv, opts);
随后调用 check_uid_before_start() 确保不是以 root 运行。
阶段 3:日志与诊断系统初始化
//【柚子解读】初始化日志系统,5 类日志文件:
// observer.log — 主运行日志(INFO 及以上)
// observer.wf.log — 警告及以上日志(WARN/ERROR)
// rootservice.log — RS(RootService)专属日志
// election.log — 选举日志(Paxos 相关)
// rs_.log — RS 选举详细日志
// 配置:异步日志,buffer 写入,定时刷盘,单文件 256MB 滚动。
ObCurTraceId::get_trace_id()->set("Y0-0000000000000001-0-0");
设置 trace_id 为固定值 Y0-0000000000000001-0-0,这是启动阶段的特殊 trace_id(非随机),标识这是系统首个请求。
阶段 4:malloc 调优
//【柚子解读】glibc malloc 行为调优,OceanBase 有自己的内存分配器 ob_allocator,
// glibc 的 mmap 分配会与 ob_allocator 竞争虚拟地址空间。
// M_MMAP_MAX=1G — mmap 上限 1GB(避免 glibc 频繁 mmap 零碎内存)
// M_ARENA_MAX=1 — 只用 1 个 arena(默认每 CPU 一个,多 arena 会导致内存碎片膨胀)
// M_MMAP_THRESHOLD — 阈值设大,小分配走 brk,大分配走 ob_allocator 而非 glibc mmap
static const int DEFAULT_MMAP_MAX_VAL = 1024 * 1024 * 1024;
mallopt(M_MMAP_MAX, DEFAULT_MMAP_MAX_VAL);
mallopt(M_ARENA_MAX, 1);
阶段 5:ObServer 对象创建与 init()
//【柚子解读】核心步骤:创建 ObServer 单例并执行完整初始化。
// ObServer 是整个 observer 进程的中枢管理器,
// init() 内部完成 16 步初始化(详见下一节 ob_server.cpp 解读)。
// ObIServerCodec/ObIServerProcessor 用于多租户 RPC 分发。
ObServer &observer = ObServer::get_instance();
observer.init(opts, log_cfg); // 如果失败会打印详细错误并退出
阶段 6:start() — 启动后台服务
//【柚子解读】start() 与 init() 的区别:
// init() = 创建对象、分配资源、建立内部数据结构(被动)
// start() = 启动后台线程、打开网络监听、开始接收请求(主动)
// 关键启动项(按顺序):
// 1. multi_tenant_.start() — 启动多租户框架
// 2. 各子模块 start()
// 3. net_frame_.start() — 打开 RPC 网络端口,开始接收客户端请求
// 4. rootserver_.start() — 启动 RS(集群管理后台线程)
observer.start();
阶段 7:wait() — 阻塞等待停止信号
//【柚子解读】主线程在此无限期阻塞,直到收到停止信号(SIGTERM/SIGINT)。
// wait() 内部用条件变量等待 stop_ 标志被设置。
// 信号处理函数将 stop_ 置为 true,wait() 随即返回。
// 之后执行 destroy() 逆序释放资源,进程退出。
observer.wait(); // 阻塞,直到收到停止信号
print_all_thread("BEFORE_DESTROY", OB_SERVER_TENANT_ID);
observer.destroy(); // 逆序释放资源
1.3 main() 函数
//【柚子解读】main 函数是 thin wrapper:
// 1. 调用 inner_main() 执行实际启动逻辑
// 2. 如果 inner_main 返回非 0(启动失败),打印错误并 exit(1)
// 3. 返回 0 表示正常退出
// 注意:OceanBase 的 main 不做 fork/daemonize,
// 进程守护化由 systemd/supervisor 等外部工具管理。
int main(int argc, char *argv[]) {
int ret = inner_main(argc, argv);
if (ret != OB_SUCCESS) {
fprintf(stderr, "observer start failed, ret=%dn", ret);
exit(1);
}
return 0;
}
二、ob_server.cpp — 初始化、启动与停机的完整生命周期
2.1 init() — 16 步初始化链
ObServer::init() 是最核心的初始化函数,约 300 行,执行 16 个有序步骤。OceanBase 的错误处理风格是“错误码瀑布”——每步用 OB_FAIL(ret) 宏检查,失败则跳过后续所有步骤,直接返回错误码:
//【柚子解读】init() 的 16 步初始化(顺序不能乱,后步依赖前步):
//
// ① init_config() — 加载配置文件 + 参数校验
// ② 判断仲裁模式 — 分叉:仲裁模式走精简初始化路径
// ③ OB_LOGGER.init() — 日志系统初始化
// ④ init_pre_setting() — 系统级预设(时区/字符集/本地化)
// ⑤ init_local_ip() — 解析本机 IP 地址
// ⑥ init_hostname() — 确认主机名
// ⑦ init_signal_event() — 注册信号处理(SIGTERM→优雅停机)
// ⑧ init_net_frame() — RPC 网络框架初始化(NetBuffer/RPC Queue)
// ⑨ init_multi_tenant() — 多租户引擎初始化(OMT 框架)
// ⑩ init_sql() — SQL 引擎初始化(parser/resolver/optimizer/executor)
// ⑪ init_storage() — 存储引擎初始化(MemTable/SSTable/LogService 接口)
// ⑫ init_root_service() — RootService 初始化(集群管理服务)
// ⑬ init_ob_service() — ObService RPC 接口初始化(节点间通信)
// ⑭ init_block_manager() — 块管理器初始化(宏块/微块管理)
// ⑮ init_dtl_() — 数据传输层 DTL 初始化(并行查询数据管道)
// ⑯ init_upgrade_executor() — 升级框架初始化(在线升级支持)
2.2 仲裁模式 vs 常规模式
//【柚子解读】仲裁模式(Arbitration Mode)是 OceanBase 4.x 的特性:
// 仲裁节点不存储用户数据,仅参与 Paxos 投票,用于打破"偶数节点"脑裂死锁。
// 场景:2 副本集群 + 1 仲裁节点 = 3 节点 Paxos Group,可容忍 1 节点故障。
// 仲裁模式 init 精简了 storage/sql 等重量级初始化,只启动仲裁服务。
if (is_arbitration_mode()) {
#ifdef OB_BUILD_ARBITRATION
FLOG_INFO("begin init observer in arbitration mode", KR(ret));
// ... 精简初始化路径
#endif
} else {
// ... 常规模式完整 16 步初始化
}
2.3 start() — 启动后台服务
//【柚子解读】start() 的核心逻辑(约 120 行):
// 1. multi_tenant_.start() — 启动多租户框架的后台线程
// (创建 500 租户线程池、启动租户级定时任务)
// 2. 各子模块递归 start()
// 3. net_frame_.start() — 绑定 RPC 端口,开始 accept 连接
// 4. rootserver_.start() — 启动 RS 后台线程(心跳/均衡/合并等定时任务)
// 关键点:start() 之后,observer 开始接受客户端请求,init→start 的转换是
// "静态就绪→动态服务"的分水岭。
int ObServer::start() {
// ...
if (FAILEDx(multi_tenant_.start())) {
LOG_ERROR("fail to start multi tenant", KR(ret));
} else {
FLOG_INFO("success to start multi tenant");
// ... 继续启动其他服务
}
}
2.4 wait() 与 destroy() — 优雅停机
//【柚子解读】wait() 的优雅停机骨架(约 60 行):
// 1. 主线程在条件变量上阻塞,等待 stop_ 标志被设置
// 2. 信号处理函数(SIGTERM/SIGINT)将 stop_ 置为 true 并 notify
// 3. wait() 返回后,调用 destroy() 逆序释放资源:
// - 先停网络(不再接新请求)
// - 再停各子模块(完成在途请求)
// - 最后释放内存/关闭文件
// 这个"信号→标志位→轮询→逆序停服"的模式保证了:
// - 收到 SIGTERM 后进程不会立即死亡(正在执行的事务能完成)
// - 不会产生半写数据(WAL 先于 MemTable flush)
// - 停机是确定性的(有明确完成检测点)
int ObServer::wait() {
// ...
while (!stop_) {
// 等待信号处理函数唤醒
stop_cond_.wait(stop_mutex);
}
}
三、五个关键 init_ 子函数速览
| 函数 | 职责 | 关键依赖 |
|---|---|---|
| init_network() | 初始化 RPC 网络层:NetBuffer 池、RPC 分发队列、Easy 框架回调 | oblib/easy(网络框架) |
| init_multi_tenant() | 初始化 OMT 多租户引擎:租户隔离线程池、租户级资源配置 | share/ob_tenant_id.h |
| init_sql() | 初始化 SQL 引擎:parser(语法解析)、resolver(语义解析)、optimizer(优化器)、executor(执行器) | sql/ 全模块 |
| init_storage() | 初始化存储引擎:MemTable(写路径)、SSTable(读路径)、LogService 接口(WAL 持久化) | storage/ + logservice/ |
| init_root_service() | 初始化 RootService:集群元数据管理、负载均衡、分区迁移、Schema 管理 | rootserver/ |
四、总结:observer 启动全景图
进程启动
│
├── main() 薄包装入口
│ └── inner_main() 7 阶段编排
│ ├── ① 信号栈 + 内存基础
│ ├── ② 参数解析 + 安全检查
│ ├── ③ 日志/诊断系统初始化
│ ├── ④ malloc 调优
│ ├── ⑤ ObServer::init() ← 核心:16 步初始化链
│ ├── ⑥ ObServer::start() ← 启动后台服务
│ └── ⑦ ObServer::wait() ← 阻塞等待停止信号
│
└── 信号 SIGTERM/SIGINT → stop_=true → wait()返回
└── destroy() 逆序释放 → 进程退出
下一篇文章将进入 SQL 编译流水线(sql 模块),从 sql/ob_sql.cpp 入口沿 parser→resolver→optimizer 主链路解读。