第1章 简介
常规的项目开发中,我们常用的API有:SQL/Table API、DataStream API。而DataStream API其实还提供了一组相对底层的转换——处理函数(ProcessFunction)。
我们可以从Flink API的三层模型中看到,ProcessFunction位于最底层,它除了最基本的功能外,还可以访问记录的timestamp和watermark,并且可以注册在将来某个特定时间触发的Timer定时器。
第2章 处理函数类型
Flink目前提供了8种不同的处理函数:
ProcessFunction:处理函数
KeyedProcessFunction:键值分区的处理函数
CoProcessFunction:双流处理函数
ProcessJoinFunction:多流join处理函数
BroadcastProcessFunction:广播流处理函数
KeyedBroadcastProcessFunction:键值分区的广播流处理函数
ProcessWindowFunction:键值分区的窗口处理函数
ProcessAllWindowFunction:非键值分区的窗口处理函数
这些函数适用于不同的上下文环境,但是他们的功能都很相似。下面我们以ProcessFunction和KeyedProcessFunction为例实现几个小demo。
第3章 基本使用
3.1 ProcessFunction
定义一个继承ProcessFunction的类
private static class ConfirmIncreaseFunctoin extends ProcessFunction<Tuple3<String, Integer, Long>,String>{
private final int threshold=100;
private ValueState<Long> currentTime;
private MapState<String,Integer> areaTime;
//...
@Override
public void onTimer(long timestamp, OnTimerContext ctx, Collector<String> out) throws Exception {
super.onTimer(timestamp, ctx, out);
// 10秒之后,输出超过100例的城市
Iterator<Map.Entry<String, Integer>> iterator = areaTime.iterator();
while (iterator.hasNext()){
Map.Entry<String, Integer> map = iterator.next();
if(map.getValue()>threshold){
out.collect(map.getKey());
areaTime.remove(map.getKey());
}
}
currentTime.clear();
}
@Override
public void processElement(Tuple3<String, Integer, Long> tp, Context context, Collector<String> collector) throws Exception {
// 城市
String city = tp.f0;
// 确诊数量
Integer num = tp.f1;
// 不存在计时器
if (areaTime.contains(city)) {
Integer times = areaTime.get(city);
Integer currentSumTimes = (times + num) > 0 ? (times + num) : 0;
if (currentSumTimes >= threshold) {
// 超过100例,设置计时器,以当前processingtime+10000毫秒进行设置
long timerTs = context.timerService().currentProcessingTime() + 10000;
context.timerService().registerProcessingTimeTimer(timerTs);
currentTime.update(timerTs);
} else {
// 小于100例
Long value = currentTime.value();
if(value !=null) {
context.timerService().deleteProcessingTimeTimer(currentTime.value());
areaTime.remove(city);
currentTime.clear();
}
}
areaTime.put(city, currentSumTimes);
}else{
areaTime.put(city, num > 0 ? num : 0);
}
}
}
在主函数中进行调用
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//模拟数据源:地区,核酸检测确诊人数,当前时间
DataStream<Tuple3<String, Integer, Long>> dataStream = env.addSource(new SourceFunction<Tuple3<String, Integer, Long>>() {
//...
});
//TODO 调用自定义的ProcessFunction处理函数
dataStream.keyBy(new KeySelector<Tuple3<String, Integer, Long>, Object>() {
@Override
public Object getKey(Tuple3<String, Integer, Long> tp) throws Exception {
return tp.f0;
}
}).process(new ConfirmIncreaseFunctoin()).print();
env.execute("Nucleic Acid Test");//核酸检测
}
3.2 KeyedProcessFunction
定义一个继承KeyedProcessFunction的类
private static class ConfirmIncreaseFunctoin extends KeyedProcessFunction<Tuple,Tuple3<String, Integer, Long>,String> {
private ValueState<Long> currentTime;
//...
@Override
public void onTimer(long timestamp, OnTimerContext ctx, Collector<String> out) throws Exception {
super.onTimer(timestamp, ctx, out);
Tuple currentKey = ctx.getCurrentKey();
out.collect(currentKey.toString());
currentTime.clear();
}
@Override
public void processElement(Tuple3<String, Integer, Long> tp, Context context, Collector<String> collector) throws Exception {
String area = tp.f0;
Integer num = tp.f1;
Long value = currentTime.value();
// 不存在计时器
if(value == null){
if(num>=3){
long timerTs = context.timerService().currentProcessingTime()+10000;
// 设置计时器,以当前processingtime+10000毫秒进行设置
context.timerService().registerProcessingTimeTimer(timerTs);
currentTime.update(timerTs);
}
}
else {
// 已经存在计时器
if(num<3) {
// 删除当前计时器
context.timerService().deleteProcessingTimeTimer(value);
currentTime.clear();
}
}
}
}
在主函数中进行调用
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//模拟数据源:地区,核酸检测确诊人数,当前时间
DataStream<Tuple3<String, Integer, Long>> dataStream = env.addSource(new SourceFunction<Tuple3<String, Integer, Long>>() {
//...
});
//TODO KeyBy分区后,调用自定义的KeyedProcessFunction处理函数
dataStream.keyBy(0)
.process(new ConfirmIncreaseFunctoin()).print();
env.execute("Nucleic Acid Test");//核酸检测
}
完整demo请查看github:https://github.com/zihaodeng/hellobigdata/tree/main/flink-online/flink-api
今天是建党100周年,引用今天听到的党员精神里的一句话:“敢于斗争,善于斗争,逢山开道、遇水架桥,勇于战胜一切风险挑战!”。继续奋斗吧少年!