本文主要讲述:
- JM 端恢复 Checkpoint 元数据流程
- JM 端为 subtask 分配 StateHandle 流程
一、 JM 端恢复 Checkpoint 元数据流程
JM 端从 Checkpoint 中恢复任务的流程是从 CheckpointCoordinator 的 restoreSavepoint 方法开始的。restoreSavepoint 方法的源码如下所示:
public boolean restoreSavepoint(
String savepointPointer,boolean allowNonRestored,
Map tasks,
ClassLoader userClassLoader) throws Exception {
// 从外部存储获取 Checkpoint 元数据,这里的 checkpointStorage 是 FsCheckpointStorage
final CompletedCheckpointStorageLocation checkpointLocation =
checkpointStorage.resolveCheckpoint(savepointPointer);
// 加载 Checkpoint 元数据,并对 Checkpoint 进行校验,
// 校验项包括 maxParallelism、allowNonRestoredState
CompletedCheckpoint savepoint = Checkpoints.loadAndValidateCheckpoint(
job, tasks, checkpointLocation, userClassLoader, allowNonRestored);
// 将要恢复的 Checkpoint 信息写入到 zk 中
completedCheckpointStore.addCheckpoint(savepoint);
// Reset the checkpoint ID counter
long nextCheckpointId = savepoint.getCheckpointID() + 1;
checkpointIdCounter.setCount(nextCheckpointId);
// 从最近一次 Checkpoint 处恢复 State
return restoreLatestCheckpointedState(tasks, true, allowNonRestored);
}
首先从 savepointPointer 所在的目录找到 _metadata 文件(Checkpoint 的元数据文件),然后生成 CompletedCheckpointStorageLocation。CompletedCheckpointStorageLocation 见名之意:已经完成的 Checkpoint 存储位置。
加载元数据及校验逻辑
然后 Checkpoints.loadAndValidateCheckpoint() 方法加载 Checkpoint 元数据,并对 Checkpoint 进行校验,校验项包括 maxParallelism、allowNonRestoredState。Checkpoints 类 loadAndValidateCheckpoint() 方法的精简版校验源码如下所示:
public static CompletedCheckpoint loadAndValidateCheckpoint(
JobID jobId,
Map tasks,
CompletedCheckpointStorageLocation location,
ClassLoader classLoader,boolean allowNonRestoredState) throws IOException {
// 从新的 ExecutionGraph 中生成 OperatorId 与 ExecutionJobVertex 的映射
Map operatorToJobVertexMapping = new HashMap<>();for (ExecutionJobVertex task : tasks.values()) {
for (OperatorID operatorID : task.getOperatorIDs()) {
operatorToJobVertexMapping.put(operatorID, task);
}
}
HashMap operatorStates = new HashMap<>(checkpointMetadata.getOperatorStates().size());// 循环检查所有的 OperatorState,这里的 OperatorState 不是指 Flink 的 OperatorState,// 而是指 算子级别的 State,这里的 Operator 指代算子for (OperatorState operatorState : checkpointMetadata.getOperatorStates()) {
// 在新的 ExecutionGraph 中找 Checkpoint 中 OperatorId 对应的算子
ExecutionJobVertex executionJobVertex =
operatorToJobVertexMapping.get(operatorState.getOperatorID());// executionJobVertex == null 说明有 OperatorState ,但找不到对应的 executionJobVertexif (executionJobVertex != null) {
// 只要新旧 maxParallelism 相同,或者 新的 maxParallelism 没有配置,都认为校验通过if (executionJobVertex.getMaxParallelism() == operatorState.getMaxParallelism()
|| !executionJobVertex.isMaxParallelismConfigured()) {
operatorStates.put(operatorState.getOperatorID(), operatorState);
} else {
// 相反 新旧 Job 的 maxParallelism 不同,// 且新 Job 的 maxParallelism 是用户手动设定的,则抛出异常,恢复失败throw new IllegalStateException(msg);
}
} else if (allowNonRestoredState) {
// 跳过了恢复流程
LOG.info("Skipping savepoint state for operator {}.");
} else {
// 不允许跳过恢复,检查当前 算子,是否包含状态,// 如果包含,则抛异常,Job 无法启动for (OperatorSubtaskState operatorSubtaskState : operatorState.getStates()) {
if (operatorSubtaskState.hasState()) {
throw new IllegalStateException(msg);
}
}
}
}// (3) convert to checkpoint so the system can fall back to it
CheckpointProperties props = CheckpointProperties.forSavepoint();return new CompletedCheckpoint(XXX);
}
loadAndValidateCheckpoint 方法的检验过程,首先遍历新 Job 的 ExecutionGraph 中,通过 ExecutionGraph 生成 OperatorId 与 ExecutionJobVertex 的映射保存到 operatorToJobVertexMapping 中。然后循环检查所有的 OperatorState,这里的 OperatorState 不是指 Flink 的 OperatorState,而是指算子级别的 State(Operator 指代算子)。
在新的 ExecutionGraph 中找 Checkpoint 中 OperatorId 对应的算子保存到 executionJobVertex 中。executionJobVertex == null 说明有 OperatorState,但在新的 ExecutionGraph 中找不到对应的 executionJobVertex。
executionJobVertex != null 的情况
executionJobVertex != null 说明当前遍历的算子在新的 ExecutionGraph 中可以找到,此时检查新旧任务的 maxParallelism 是否可以匹配。只要新旧 maxParallelism 相同或者新的 maxParallelism 没有配置,都认为校验通过。这里 maxParallelism 没有设置指的是 maxParallelism 可能是 Flink 引擎自动生成的,而不是用户在代码中手动设置的。相反新旧 Job 的 maxParallelism 不同,且新 Job 的 maxParallelism 是用户手动设定的,则抛出异常,恢复失败。这也是在 《从 KeyGroup 到 Rescale》文章中讲到的,maxParallelism 不同,任务不能恢复。
executionJobVertex == null 的情况
executionJobVertex == null 说明有 OperatorState,但在新的 ExecutionGraph 中找不到对应的 executionJobVertex。这种现象说明 Flink 任务的代码发生了变动,删除了一些有状态算子,使得 Checkpoint 中保存的一些 State 不能正常恢复了。
executionJobVertex == null 的情况要对 allowNonRestoredState 参数进行检查,allowNonRestoredState 表示跳过那些无法映射到新程序的状态。如果 allowNonRestoredState 设置为 true,则 Flink 会跳过恢复当前算子的 State;如果 allowNonRestoredState 设置为 false,Flink 会检查当前算子是否有 State,如果有 State 则抛出异常,任务恢复失败。
恢复元数据
校验流程结束后,会将本次 Checkpoint 信息写入 zk,便于从 Checkpoint 中恢复。
restoreLatestCheckpoin