想象这样一种情景,我们想在算子或者函数中获取数据流中Watermark的时间戳,或者在时间上前后穿梭,我们该如何办?
ProcessFunction
系列函数给我们提供了这样子的能力,它们是Flink体系中最底层的API,提供了对数据流更细粒度的操作权限。Flink SQL是基于这些函数实现的,一些需要高度个性化的业务场景也需要使用这些函数。这个系列函数主要包括KeyedProcessFunction
、ProcessFunction
、CoProcessFunction
、KeyedCoProcessFunction
、ProcessJoinFunction
等多种函数,这些函数都继承于RichFunction,可以获取状态信息,另外都有定时器,可以在时间维度上设计复杂的业务逻辑。
ProcessFunction介绍
ProcessFunction
是flink提供面向使用者low-level层级的api,通过ProcessFunction可以访问state、注册处理时间/事件时间定时器来帮助我们完成一些比较复杂的操作,但是只能用使用在keyedStream中,这是因为根据getRuntimeContext 得到的StreamingRuntimeContext只提供了KeyedStateStore的访问许可权,所以只能访问keyd state,;另外注册的定时器必须是与key相关,也就解释了在ProcessFunction中只能在keyedStream做定时器注册。ProcessFunction源码中定义如下:
// org.apache.flink.streaming.api.functions.ProcessFunction
public abstract class ProcessFunction<I, O> extends AbstractRichFunction {
private static final long serialVersionUID = 1L;
// 处理每一条数据的逻辑
public abstract void processElement(I value, Context ctx, Collector<O> out)
throws Exception;
// 当定义的timer触发时候进行回调
public void onTimer(long timestamp, OnTimerContext ctx, Collector<O> out)
throws Exception {
}
public abstract class Context {
public abstract Long timestamp();
public abstract TimerService timerService(); // 可以注册一个定时器
public abstract <X> void output(OutputTag<X> outputTag, X value);
}
public abstract class OnTimerContext extends Context {
public abstract TimeDomain timeDomain();
}
}
state
ProcessFunction继承了AbstractRichFunction,可以访问Flink中的keyed state,可以通过其访问 RuntimeContext
,获取相应的状态信息。
Timer
定时器允许应用程序对processing time
和 event_time
的变化作出反应。每次调用该函数processElement(...)
都会获得一个Context
对象,该对象可以访问元素的事件时间戳和TimerService,这也是区别于FlatMapRichFunction等普通函数的地方,可以通过改Context获取时间戳,设置Timer,TimerService
可用于事件时间/处理时间实例注册回调。
onTimer
当注册了事件定时器,达到计时器的特定时间时,方法onTimer(...)
将会被自动调用。在该调用期间,所有状态再次限定为创建计时器的key的状态,允许计时器操纵keyed state
。
综上,processFunction使用模式一般为:
stream.keyBy(...).process(new