OceanBase源码解读:observer启动流程

引言

本系列文章是对 OceanBase v4.3.5_CE_BP6 开源源码的逐模块深度解读。本文解读 observer 进程的启动流程,覆盖 main.cppob_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 主链路解读。


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注