1. 简介
分布式有状态的流处理支持在云端部署和执行大规模连续地计算,同时可以应对低延迟和高吞吐量场景。
这一模式最大的挑战之一是在系统可能失败的情况下提供数据可靠性保证。
现有方法依赖于可用于故障恢复的周期性全局状态快照。
此类方法有两个主要缺点:首先,它们通常会阻塞计算;其次,它们通常会保存传输中的所有记录和操作状态,这导致更多的快照数据。
而异步屏障快照(ABS)是一种适用于现代流处理引擎的轻量级算法,可最大限度地减少空间需求。Apache Flink上实现了ABS,评估的结果表明,ABS不会对执行产生重大影响,保持线性可扩展性并且能够在频繁快照的情况下表现良好。
2. 异步屏障快照原理描述
流式数据处理计算图中的节点可分为三类:Source(负责数据输入)、Sink(负责结果输出)和算子,它们之间由数据传输通道连接。
以T表示计算节点的集合,E表示边(数据传输通道)的集合,则计算图可表示为G=(T,E)
一个很自然的想法是对计算图在某些时间点上做快照,这样在故障发生后整个数据处理系统可以恢复到某个快照时间点的状态,以保证exactly once语义。
ABS的步骤如下:
- 引擎定期向Source节点插入检查点屏障(Barrier)。在收到作为控制消息的检查点屏障后,Source节点对自己的状态做快照,并在其输出通道上广播此检查点屏障消息。此外,不同的检查点屏障可以通过id区分。
- 当其从任意一个输入通道收到检查点屏障消息时,算子或Sink节点阻塞此输入通道,直至本节点从所有输入通道收到检查点屏障。
- 在其从所有输入通道收到检查点屏障后,算子或Sink节点对自己的状态做快照,然后对其所有输入通道解除阻塞。
3. 源码分析
JobManager 根据 JobGraph 生成ExecutionGraph。ExecutionGraph是JobGraph的并行化版本,是调度层最核心的数据结构。
public void enableCheckpointing(
long interval,
long checkpointTimeout,
long minPauseBetweenCheckpoints,
int maxConcurrentCheckpoints,
CheckpointRetentionPolicy retentionPolicy,
List<ExecutionJobVertex> verticesToTrigger,
List<ExecutionJobVertex> verticesToWaitFor,
List<ExecutionJobVertex> verticesToCommitTo,
List<MasterTriggerRestoreHook<?>> masterHooks,
CheckpointIDCounter checkpointIDCounter,
CompletedCheckpointStore checkpointStore,
StateBackend checkpointStateBackend,
CheckpointStatsTracker statsTracker) {
// simple sanity checks
checkArgument(interval >= 10, "checkpoint interval must not be below 10ms");
checkArgument(checkpointTimeout >= 10, "checkpoint timeout must not be below 10ms");
checkState(state == JobStatus.CREATED, "Job must be in CREATED state");
checkState(checkpointCoordinator == null, "checkpointing already enabled");
ExecutionVertex[] tasksToTrigger = collectExecutionVertices(verticesToTrigger);
ExecutionVertex[] tasksToWaitFor = collectExecutionVertices(verticesToWaitFor);
ExecutionVertex[] tasksToCommitTo = collectExecutionVertices(verticesToCommitTo);
checkpointStatsTracker = checkNotNull(statsTracker, "CheckpointStatsTracker");
// create the coordinator that triggers and commits checkpoints and holds the state
// 参数太多,如果用builder模式会好些
checkpointCoordinator = new CheckpointCoordinator(
jobInformation.getJobId(),
interval,
checkpointTimeout,
minPauseBetweenCheckpoints,
maxConcurrentCheckpoints,
retentionPolicy,
tasksToTrigger,
tasksToWaitFor,
tasksToCommitTo,
checkpointIDCounter,
checkpointStore,
checkpointStateBackend,
ioExecutor,
SharedStateRegistry.DEFAULT_FACTORY);
// register the master hooks on the checkpoint coordinator
for (MasterTriggerRestoreHook<?> hook : masterHooks) {
if (!checkpointCoordinator.addMasterHook(hook)) {
LOG.warn("Trying to register multiple checkpoint hooks with the name: {}", hook.getIdentifier());
}
}
checkpointCoordinator.setCheckpointStatsTracker(checkpointStatsTracker);
// interval如果是Long.MAX_VALUE表示禁用周期性checkpoint
if (interval != Long.MAX_VALUE) {
// job status changes (running -> on, all other states -> off)
registerJobStatusListener(checkpointCoordinator.createActivatorDeactivator());
}
}
- enableCheckpointing方法负责初始化CheckpointCoordinator并注册JobStatusListener
- interval等于Long.MAX_VALUE是,表示禁用了周期性checkpoint
public JobStatusListener createActivatorDeactivator() {
synchronized (lock) {
if (shutdown) {
throw new IllegalArgumentException("Checkpoint coordinator is shut down");
}
if (jobStatusListener == null) {
jobStatusListener = new CheckpointCoordinatorDeActivator(this);
}
return jobStatusListener;
}
}
- CheckpointCoordinator类createActivatorDeactivator方法返回jobStatusListener
- lock是个对象锁,粒度是CheckpointCoordinator实例级别
@Override
public void jobStatusChanges(JobID jobId, JobStatus newJobStatus, long timestamp, Throwable error) {