要使用Manager(托管) Operator State
,需要实现CheckpointedFunction
接口或者ListCheckpointed
接口。
CheckpointedFunction
接口提供具有不同重新分发Non-Keyed State
的访问状态。他需要实现2个方法:
void snapshotState(FunctionSnapshotContext context) throws Exception;
void initializeState(FunctionInitializationContext context) throws Exception;
每当必须执行checkpoint时,都会有snapshotState()
方法调用。initializeState()
方法首次初始化函数或从早期的checkpoint中恢复时被调用。
目前,支持列表样式的托管Operator State。状态被预期是一个List
的序列化的对象,彼此独立的,因而在重新缩放获再分配。换句话说,这些对象是可以重新分配非键控状态的最精细的粒度。根据状态访问方法,定义了以下重新分发方案:
element1
和element2
,当将并行性增加到2时,element1
可能最终在运算符实例0分区中,而element2
将转到运算符实例1分区。下面是一个有状态的示例SinkFunction
,用于CheckpointedFunction
在将元素发送到外部世界之前对其进行缓冲。它演示了基本的偶分裂再分发列表状态:
public class BufferingSink implements SinkFunction<Tuple2<String, Integer>>, CheckpointedFunction {
private final int threshold;
private transient ListState<Tuple2<String, Integer>> checkpointedState;
private List<Tuple2<String, Integer>> bufferedElements;
public BufferingSink(int threshold) {
this.threshold = threshold;
this.bufferedElements = new ArrayList<>();
}
@Override
public void invoke(Tuple2<String, Integer> value, Context contex) throws Exception {
bufferedElements.add(value);
if (bufferedElements.size() == threshold) {
for (Tuple2<String, Integer> element : bufferedElements) {
// send it to the sink
}
bufferedElements.clear();
}
}
@Override
public void snapshotState(FunctionSnapshotContext context) throws Exception {
checkpointedState.clear();
for (Tuple2<String, Integer> element : bufferedElements) {
checkpointedState.add(element);
}
}
@Override
public void initializeState(FunctionInitializationContext context) throws Exception {
ListStateDescriptor<Tuple2<String, Integer>> descriptor = new ListStateDescriptor<>("buffered-elements", TypeInformation.of(new TypeHint<Tuple2<String, Integer>>() {
}));
checkpointedState = context.getOperatorStateStore().getListState(descriptor);
if (context.isRestored()) {// isRestored:检查是否是失败后恢复
for (Tuple2<String, Integer> element : checkpointedState.get()) {
bufferedElements.add(element);
}
}
}
}
如果要在还原时使用联合重新分发方案的列表状态,可以使用getUnoinListState(descriptor)
方法获取。如果是getListState(descriptor)
,它仅表示将使用基本偶分裂再分配方案。
该ListCheckpointed
接口是CheckpointedFunction的变体。它仅支持与恢复甚至分裂的再分配方案列表式的状态。它还需要实现两种方法:
List<T> snapshotState(long checkpointId, long timestamp) throws Exception;
void restoreState(List<T> state) throws Exception;
在snapshotState()
操作人员应对象返回检查点的列表,并 restoreState
具有处理后恢复这样的列表。如果状态不是重新分区,可以随时返回Collections.singletonList(MY_STATE)
的snapshotState()
。
下面使用自定义SourceFunction实现ListCheckpointed接口:
public class CustomSourceFunction extends RichParallelSourceFunction<Long> implements ListCheckpointed<Long> {
/**
* current offset for exactly once semantics
*/
private Long offset = 0L;
/**
* flag for job cancellation
*/
private volatile boolean isRunning = true;
@Override
public void run(SourceContext<Long> ctx) {
final Object lock = ctx.getCheckpointLock();
while (isRunning) {
// output and state update are atomic
synchronized (lock) {
ctx.collect(offset);
offset += 1;
}
}
}
@Override
public void cancel() {
isRunning = false;
}
@Override
public List<Long> snapshotState(long checkpointId, long checkpointTimestamp) {
return Collections.singletonList(offset);
}
@Override
public void restoreState(List<Long> state) {
for (Long s : state) {
offset = s;
}
}
}