CheckpointedFunction

博客主要介绍了Flink中的CheckpointedFunction和CheckpointListener接口。CheckpointedFunction提供特定方法,CheckpointListener可在检查点完成后通知。还分析了FlinkKafkaConsumerBase和AbstractStreamOperator,前者实现CheckpointedFunction接口,后者实现StreamOperator接口,同时介绍了状态后端的相关情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CheckpointedFunction&CheckpointListener

作为一个接口,CheckpointedFunction提供如下两个方法:

//获得function或者operator的当前状态(快照)
void snapshotState(FunctionSnapshotContext context) throws Exception;
//用于从之前的检查点中恢复function或operator的状态
void initializeState(FunctionInitializationContext context) throws Exception;

继承关系如下图:
在这里插入图片描述
CheckpointListener是一个监听器,它会在某个检查点完成之后给出通知,客户程序可以订阅它然后进行相应的回调处理notifyCheckpointComplete
在这里插入图片描述

FlinkKafkaConsumerBase

FlinkKafkaConsumerBase是Kafka Source的基础类,内部实现了CheckpointedFunction接口,这属于Operator State的一种,下面我们看看具体方法是如何实现CheckPoint的

//FlinkKafkaConsumerBase
	public final void initializeState(FunctionInitializationContext context) throws Exception {
		OperatorStateStore stateStore = context.getOperatorStateStore();
		//从stateStore中恢复上一次提交的位置
		ListState<Tuple2<KafkaTopicPartition, Long>> oldRoundRobinListState =
		stateStore.getSerializableListState(DefaultOperatorStateBackend.DEFAULT_OPERATOR_STATE_NAME);
		this.unionOffsetStates = stateStore.getUnionListState(new ListStateDescriptor<>(
				OFFSETS_STATE_NAME,
				TypeInformation.of(new TypeHint<Tuple2<KafkaTopicPartition, Long>>() {})));
		if (context.isRestored() && !restoredFromOldState) {
			restoredState = new TreeMap<>(new KafkaTopicPartition.Comparator());
			for (Tuple2<KafkaTopicPartition, Long> kafkaOffset : oldRoundRobinListState.get()) {
				restoredFromOldState = true;
				unionOffsetStates.add(kafkaOffset);
			}
			oldRoundRobinListState.clear();
			}
			for (Tuple2<KafkaTopicPartition, Long> kafkaOffset : unionOffsetStates.get()) {
				restoredState.put(kafkaOffset.f0, kafkaOffset.f1);
			}
		} 
		...
	}

    public final void snapshotState(FunctionSnapshotContext context) throws Exception {
		if (!running) {
			...
		} else {
		//清空unionOffsetStates
			unionOffsetStates.clear();
			final AbstractFetcher<?, ?> fetcher = this.kafkaFetcher;
			//fetcher为空
			if (fetcher == null) {
				for (Map.Entry<KafkaTopicPartition, Long> subscribedPartition : subscribedPartitionsToStartOffsets.entrySet()) {
				//遍历subscribedPartitionsToStartOffsets,将key和value加入unionOffsetStates
					unionOffsetStates.add(Tuple2.of(subscribedPartition.getKey(), subscribedPartition.getValue()));
				}
				//如果提交模式为ON_CHECKPOINTS,只有当快照已经完成才会提交offset
				//将restoredState加入pendingOffsetsToCommit中
				if (offsetCommitMode == OffsetCommitMode.ON_CHECKPOINTS) {
					pendingOffsetsToCommit.put(context.getCheckpointId(), restoredState);
				}
			} else {
			//fetcher不为空,从snapshotCurrentState中获取当前的offset
				HashMap<KafkaTopicPartition, Long> currentOffsets = fetcher.snapshotCurrentState();
				if (offsetCommitMode == OffsetCommitMode.ON_CHECKPOINTS) {
					pendingOffsetsToCommit.put(context.getCheckpointId(), currentOffsets);
				}
				for (Map.Entry<KafkaTopicPartition, Long> kafkaTopicPartitionLongEntry : currentOffsets.entrySet()) {
					unionOffsetStates.add(
							Tuple2.of(kafkaTopicPartitionLongEntry.getKey(), kafkaTopicPartitionLongEntry.getValue()));
				}
			}
			if (offsetCommitMode == OffsetCommitMode.ON_CHECKPOINTS) {
			//当pendingOffsetsToCommit大小超过MAX_NUM_PENDING_CHECKPOINTS默认100时,删除第1个元素,防止发生OOM
				while (pendingOffsetsToCommit.size() > MAX_NUM_PENDING_CHECKPOINTS) {
					pendingOffsetsToCommit.remove(0);
				}
			}
		}
	}

	public final void notifyCheckpointComplete(long checkpointId) throws Exception {
		if (!running) {
			//返回
		}
		final AbstractFetcher<?, ?> fetcher = this.kafkaFetcher;
		if (fetcher == null) {
			//返回
		}
		if (offsetCommitMode == OffsetCommitMode.ON_CHECKPOINTS) {
		...
			try {
				//查找checkpointId对应的index
				final int posInMap = pendingOffsetsToCommit.indexOf(checkpointId);
				if (posInMap == -1) {
					//返回
				}
				@SuppressWarnings("unchecked")
				//移除该索引对应的快照信息
				Map<KafkaTopicPartition, Long> offsets =
					(Map<KafkaTopicPartition, Long>) pendingOffsetsToCommit.remove(posInMap);
				//所有的检查点都是按时间递增有序的
				for (int i = 0; i < posInMap; i++) {
					pendingOffsetsToCommit.remove(0);
				}
				if (offsets == null || offsets.size() == 0) {
					//返回
				}
				//提交offset到kafka
				fetcher.commitInternalOffsetsToKafka(offsets, offsetCommitCallback);
			} catch (Exception e) {
				if (running) {
					throw e;
				}
			}
		}
	}

AbstractStreamOperator

AbstractStreamOperator是所有stream operator的基类,实现了StreamOperator接口,StreamOperator只实现了CheckpointListener,并没有实现CheckpointedFunction,为什么呢?
在StreamOperator中也定义了snapshotState和initializeState方法。看下AbstractStreamOperator中这两个方法以及notifyCheckpointComplete是如何实现的。

//AbstractStreamOperator
	public final OperatorSnapshotFutures snapshotState(long checkpointId, long timestamp, CheckpointOptions checkpointOptions,
			CheckpointStreamFactory factory) throws Exception {
			...
		try (StateSnapshotContextSynchronousImpl snapshotContext = new StateSnapshotContextSynchronousImpl(
				checkpointId,
				timestamp,
				factory,
				keyGroupRange,
				getContainingTask().getCancelables())) {
			snapshotState(snapshotContext);
			//利用DefaultOperatorStateBackend来保存快照
			if (null != operatorStateBackend) {
				snapshotInProgress.setOperatorStateManagedFuture(
					operatorStateBackend.snapshot(checkpointId, timestamp, factory, checkpointOptions));
			}
			//利用第三方的backend来保存快照,如内存或RocksDb
			if (null != internalStateBackend) {
				snapshotInProgress.setKeyedStateManagedFuture(
					internalStateBackend.snapshot(checkpointId, timestamp, factory, checkpointOptions));
			}
		} catch (Exception snapshotException) {
			...
		}
		return snapshotInProgress;
	}

	public void snapshotState(StateSnapshotContext context) throws Exception {
		if (getKeyContext() != null) {
			KeyedStateCheckpointOutputStream out;
			try {
				out = context.getRawKeyedOperatorStateOutput();
			} catch (Exception exception) {
			}
			try {
				KeyGroupsList allKeyGroups = out.getKeyGroupList();
				for (int keyGroupIdx : allKeyGroups) {
					out.startNewKeyGroup(keyGroupIdx);
					timeServiceManager.snapshotStateForKeyGroup(
						new DataOutputViewStreamWrapper(out), keyGroupIdx);
				}
			} catch (Exception exception) {
			} finally {
			}
		}
	}
	
	public void notifyCheckpointComplete(long checkpointId) throws Exception {
	//如果internalStateBackend 不为空,则调用internalStateBackend的notifyCheckpointComplete来通知检查点已完成
		if (internalStateBackend != null) {
			internalStateBackend.notifyCheckpointComplete(checkpointId);
		}
	}

operatorStateBackend为DefaultOperatorStateBackend,用来保存Operator的状态,只保存在内存中,internalStateBackend包含HeapInternalStateBackend和RocksDBInternalStateBackend,用来保存KeydState

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值