功能点 1:项目骨架与启动流程 ------ 源码阅读笔记
对应源码阅读计划功能点 1:理解 Fluss 的模块组织、CoordinatorServer 和 TabletServer 的完整启动流程、Standby/Leader 状态机、ZooKeeper 注册机制。
笔记 1.1:CoordinatorServer 完整启动链路
文件:CoordinatorServer.java
路径 :fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorServer.java
1. 入口与主流程
java
public static void main(String[] args) {
Configuration configuration =
loadConfiguration(args, CoordinatorServer.class.getSimpleName());
applyServerDefaultConfigurations(configuration);
CoordinatorServer coordinatorServer = new CoordinatorServer(configuration);
startServer(coordinatorServer);
}
调用链:
main()
→ loadConfiguration(args) ← 从 args/fluss-conf.yaml 加载配置
→ applyServerDefaultConfigurations()
→ new CoordinatorServer(conf) ← 构造函数:生成 serverId(UUID) + 验证配置
→ startServer(server) ← 继承自 ServerBase,触发 startServices()
→ startServices()
→ electCoordinatorLeaderAsync() ★ 核心启动逻辑
2. 两阶段启动:Standby → Leader
这是 CoordinatorServer 最核心的设计模式------先以 Standby 身份启动基础设施,再竞选 Leader 加载协调逻辑。
java
private void electCoordinatorLeaderAsync() throws Exception {
// 阶段 1:Standby 模式启动
initCoordinatorStandby();
// 阶段 2:通过 ZooKeeper 竞选 Leader
// 成功后回调 → initCoordinatorLeader()
// 失败后回调 → cleanupCoordinatorLeader()
coordinatorLeaderElection.startElectLeaderAsync(
() -> {
try {
initCoordinatorLeader();
} catch (Exception e) {
throw new RuntimeException(e);
}
},
(Throwable t) -> {
try {
cleanupCoordinatorLeader();
} catch (Exception e) {
LOG.error("Failed to cleanup coordinator leader services", e);
}
});
}
设计思想解析:
Server 启动
│
├── 1. initCoordinatorStandby()
│ ├── ZK 连接 (ephemeral node 注册)
│ ├── RPC Server 启动(只响应健康检查)
│ ├── MetadataManager 初始化(只读模式)
│ ├── Scheduler / IO Executor 启动
│ ├── MetricRegistry + 监控指标
│ ├── Authorizer 初始化
│ └── DynamicConfigManager 启动(监听配置变更)
│
└── 2. 竞选 Leader(异步)
├── [竞选成功] → initCoordinatorLeader()
│ ├── ZK Fence (epoch +1) ← 防脑裂
│ ├── RpcClient 创建 ← Leader 需要主动连接 TabletServer
│ ├── CoordinatorEventProcessor 启动 ← 核心事件循环
│ ├── AutoPartitionManager 启动
│ ├── CoordinatorChannelManager 创建
│ └── createDefaultDatabase() ← 创建默认数据库
│
└── [失去Leader] → cleanupCoordinatorLeader()
├── 关闭 RpcClient
├── 关闭 EventProcessor
├── 关闭 ChannelManager
└── 恢复 DynamicConfigManager 监听
→ 回到 Standby 状态,等待下次竞选
3. 脑裂防护:ZK Fence 机制
java
protected void initCoordinatorLeader() throws Exception {
// ★ 关键步骤:通过 ZK epoch 防止脑裂
ZkEpoch zkEpoch = zkClient.fenceBecomeCoordinatorLeader(serverId);
registerCoordinatorLeader();
// ...
CoordinatorContext coordinatorContext = new CoordinatorContext(zkEpoch);
// 后续所有操作都携带这个 epoch,旧 Leader 的请求会被拒绝
}
工作原理:
- ZK 上有一个递增的 epoch 值
- 新 Leader 竞选成功时,epoch +1
- 旧 Leader 如果还没意识到自己已不再是 Leader,其携带旧 epoch 的写操作会被 ZK 拒绝
CoordinatorContext保存当前 epoch,传递给CoordinatorEventProcessor等组件
4. 关键设计模式
| 模式 | 实现位置 | 目的 |
|---|---|---|
| GuardedBy 锁保护 | synchronized(lock) + @GuardedBy("lock") |
所有 20+ 个服务组件在启动/关闭时线程安全 |
| CompletableFuture 链 | terminationFuture |
异步关闭流程 |
| AtomicBoolean 防重入 | isShutDown |
确保 shutdown 只执行一次 |
| 重试模式 | registerToZookeeperWithRetry() |
ZK 临时节点可能残留,需要重试注册 |
| 防御式清理 | try-catch 包裹每个 cleanup 步骤 |
任何一个清理失败不影响其余 |
5. 默认数据库创建逻辑
java
private void createDefaultDatabase() {
List<String> databases = metadataManager.listDatabases();
if (databases.isEmpty()) {
metadataManager.createDatabase("fluss", DatabaseDescriptor.EMPTY, true);
}
if (conf.get(ConfigOptions.KAFKA_ENABLED)) {
String kafkaDB = conf.get(ConfigOptions.KAFKA_DATABASE);
if (!databases.contains(kafkaDB)) {
metadataManager.createDatabase(kafkaDB, DatabaseDescriptor.EMPTY, true);
}
}
}
首次启动时自动创建 fluss 默认数据库。若启用了 Kafka 兼容模式,还会额外创建 Kafka 默认数据库。
笔记 1.2:TabletServer 启动流程
文件:TabletServer.java
路径 :fluss-server/src/main/java/org/apache/fluss/server/tablet/TabletServer.java
启动流程
TabletServer 的启动比 CoordinatorServer 简单------它不需要 Leader 选举,启动后直接向 Coordinator 注册即可:
java
// TabletServer 启动链路(简化)
startServices()
→ 1. 初始化 ZK 连接
→ 2. 创建 RpcServer(处理客户端读写请求)
→ 3. 初始化 LogManager(管理所有 LogTablet)
→ 4. 初始化 KvManager(管理所有 KvTablet/RocksDB 实例)
→ 5. 初始化 ReplicaManager(管理 Leader/Follower 角色)
→ 6. 初始化 MetricRegistry
→ 7. 向 ZK 注册 TabletServer 节点
→ 8. 开始接收 Coordinator 下发的 Tablet 加载指令
TabletServer 与 CoordinatorServer 的交互
TabletServer 启动
│
├── 注册到 ZK (ephemeral node)
│ 路径: /fluss/tablet_servers/{server_id}
│
├── CoordinatorEventProcessor 检测到新节点
│ └── 触发 Rebalance / Tablet 分配
│
└── Coordinator 下发 LoadTablet RPC
└── TabletServer.loadTablet(assignment)
├── 加载 LogTablet(创建/打开日志段)
├── 如果是 PK 表 → 加载 KvTablet(打开 RocksDB)
└── 如果是 Follower → 启动 ReplicaFetcher
关键差异:
- CoordinatorServer:有状态机(Standby ↔ Leader),启动需要竞选
- TabletServer:无状态机,启动即服务,被动接收 Tablet 分配
笔记 1.3:ServerBase 基类与模块组织
文件:ServerBase.java + pom.xml
模块依赖关系
从 pom.xml 可以推导出模块依赖层级:
fluss-dist (发行版)
└── fluss-server (服务端)
├── fluss-common (公共组件:配置、元数据、Arrow 工具)
├── fluss-rpc (Protobuf + Netty RPC)
├── fluss-client (客户端 SDK)
├── fluss-metrics (监控指标)
└── fluss-filesystems (S3/HDFS 文件系统)
fluss-flink
└── fluss-flink-common (Flink Connector 公共模块)
├── fluss-flink-1.20 (Flink 1.20 适配)
├── fluss-flink-2.2 (Flink 2.2 适配)
└── fluss-flink-tiering (Flink 驱动的 Tiering)
fluss-spark
└── Spark Connector
fluss-kafka
└── Kafka 协议兼容层
ServerBase 核心职责
java
public abstract class ServerBase {
protected final Configuration conf;
protected final PluginManager pluginManager;
// 模板方法模式
protected abstract void startServices() throws Exception; // 子类实现
protected abstract CompletableFuture<Result> closeAsync(Result result);
// 通用工具
protected static Configuration loadConfiguration(String[] args, String serverName);
protected static void startServer(ServerBase server);
protected static void applyServerDefaultConfigurations(Configuration conf);
}
设计分析:
ServerBase是所有服务端组件的抽象基类,定义了生命周期模板startServices()是抽象方法,由CoordinatorServer和TabletServer各自实现- 配置加载、JVM 参数、默认值设置都在基类中统一处理
阅读小结
| 已理解 | 尚未深入 |
|---|---|
| ✅ CoordinatorServer 的 Standby→Leader 两阶段启动 | ⬜ ZkEpoch 的具体实现 |
| ✅ 脑裂防护的 ZK Fence 机制 | ⬜ ZK 临时节点注册/重试的细节 |
| ✅ 关键组件的初始化顺序和依赖关系 | ⬜ PluginManager 的加载机制 |
| ✅ TabletServer 的启动流程和被动加载模式 | ⬜ 具体的 RPC 协议定义(Protobuf) |
| ✅ 模块依赖关系 | ⬜ CoordinatorLeaderElection 内部实现 |
下一步 :进入功能点 2------深入 MetadataManager 和 TableManager,理解表的创建、修改、删除的完整生命周期。