目录
package processfunction
import org.apache.flink.streaming.api.functions.source.{RichSourceFunction, SourceFunction}
import org.apache.flink.streaming.api.functions.{KeyedProcessFunction, ProcessFunction}
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector
import source.{ClickSource, Event}
/**
*
* @PROJECT_NAME: flink1.13
* @PACKAGE_NAME: processfunction
* @author: 赵嘉盟-HONOR
* @data: 2023-11-23 23:48
* @DESCRIPTION
*
*/
object TimeTimer {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
val data = env.addSource(new ClickSource).assignAscendingTimestamps(_.timestamp)
//TODO 基于处理时间的定时器
data.keyBy(data=>"data").process(new KeyedProcessFunction[String,Event,String] {
override def processElement(i: Event, context: KeyedProcessFunction[String, Event, String]#Context, collector: Collector[String]): Unit = {
val currentTime = context.timerService().currentProcessingTime()
collector.collect("数据到达,当前时间是:"+currentTime)
context.timerService().registerProcessingTimeTimer(currentTime+5000L)
}
override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[String, Event, String]#OnTimerContext, out: Collector[String]): Unit = {
out.collect("定时器触发,触发时间为:"+timestamp)
}
}).print("ProcessingTimeTimer")
//TODO 基于事件时间的定时器
val data1=env.addSource(new EventSource).assignAscendingTimestamps(_.timestamp)
data1.keyBy(data => "data").process(new KeyedProcessFunction[String, Event, String] {
override def processElement(i: Event, context: KeyedProcessFunction[String, Event, String]#Context, collector: Collector[String]): Unit = {
val currentTime = context.timerService().currentWatermark()
collector.collect(s"数据到达,当前时间是:$currentTime ,当前数据时间戳是 ${i.timestamp}")
context.timerService().registerEventTimeTimer(i.timestamp + 5000L)
}
override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[String, Event, String]#OnTimerContext, out: Collector[String]): Unit = {
out.collect("定时器触发,触发时间为:" + timestamp)
}
}).print("EventTimeTimer")
env.execute("ProcessingTimeTimer")
}
class EventSource extends RichSourceFunction[Event] {
override def run(sourceContext: SourceFunction.SourceContext[Event]): Unit = {
sourceContext.collect(Event("Mary","./root",100L))
Thread.sleep(5000L)
sourceContext.collect(Event("Mary", "./root", 200L))
Thread.sleep(5000L)
sourceContext.collect(Event("Mary", "./root", 1000L))
Thread.sleep(5000L)
sourceContext.collect(Event("Mary", "./root", 6000L))
Thread.sleep(5000L)
sourceContext.collect(Event("Mary", "./root", 6001L))
Thread.sleep(5000L)
}
override def cancel(): Unit = ???
}
}
这段代码展示了如何使用 Apache Flink 的 KeyedProcessFunction 来实现基于 处理时间(Processing Time) 和 事件时间(Event Time) 的定时器。以下是代码的详细解释和背景知识拓展。
代码解释
1. 环境设置
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
StreamExecutionEnvironment.getExecutionEnvironment
:获取流处理执行环境。env.setParallelism(1)
:设置并行度为 1,方便调试和观察结果。
2. 基于处理时间的定时器
val data = env.addSource(new ClickSource).assignAscendingTimestamps(_.timestamp)
data.keyBy(data => "data").process(new KeyedProcessFunction[String, Event, String] {
override def processElement(i: Event, context: KeyedProcessFunction[String, Event, String]#Context, collector: Collector[String]): Unit = {
val currentTime = context.timerService().currentProcessingTime()
collector.collect("数据到达,当前时间是:" + currentTime)
context.timerService().registerProcessingTimeTimer(currentTime + 5000L)
}
override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[String, Event, String]#OnTimerContext, out: Collector[String]): Unit = {
out.collect("定时器触发,触发时间为:" + timestamp)
}
}).print("ProcessingTimeTimer")
addSource(new ClickSource)
:从自定义数据源ClickSource
读取数据。assignAscendingTimestamps(_.timestamp)
:为数据分配时间戳。keyBy(data => "data")
:将所有数据分到同一个 key 中。process
:使用KeyedProcessFunction
处理数据。processElement
:处理每条数据,获取当前处理时间,并注册一个 5 秒后的定时器。onTimer
:定时器触发时执行的操作。
print
:打印结果。
3. 基于事件时间的定时器
val data1 = env.addSource(new EventSource).assignAscendingTimestamps(_.timestamp)
data1.keyBy(data => "data").process(new KeyedProcessFunction[String, Event, String] {
override def processElement(i: Event, context: KeyedProcessFunction[String, Event, String]#Context, collector: Collector[String]): Unit = {
val currentTime = context.timerService().currentWatermark()
collector.collect(s"数据到达,当前时间是:$currentTime ,当前数据时间戳是 ${i.timestamp}")
context.timerService().registerEventTimeTimer(i.timestamp + 5000L)
}
override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[String, Event, String]#OnTimerContext, out: Collector[String]): Unit = {
out.collect("定时器触发,触发时间为:" + timestamp)
}
}).print("EventTimeTimer")
addSource(new EventSource)
:从自定义数据源EventSource
读取数据。assignAscendingTimestamps(_.timestamp)
:为数据分配时间戳。keyBy(data => "data")
:将所有数据分到同一个 key 中。process
:使用KeyedProcessFunction
处理数据。processElement
:处理每条数据,获取当前水位线(Watermark),并注册一个 5 秒后的定时器。onTimer
:定时器触发时执行的操作。
print
:打印结果。
4. 自定义数据源
class EventSource extends RichSourceFunction[Event] {
override def run(sourceContext: SourceFunction.SourceContext[Event]): Unit = {
sourceContext.collect(Event("Mary", "./root", 100L))
Thread.sleep(5000L)
sourceContext.collect(Event("Mary", "./root", 200L))
Thread.sleep(5000L)
sourceContext.collect(Event("Mary", "./root", 1000L))
Thread.sleep(5000L)
sourceContext.collect(Event("Mary", "./root", 6000L))
Thread.sleep(5000L)
sourceContext.collect(Event("Mary", "./root", 6001L))
Thread.sleep(5000L)
}
override def cancel(): Unit = ???
}
run
:模拟数据生成,每隔 5 秒发送一条数据。cancel
:取消数据生成(未实现)。
5. 任务执行
env.execute("ProcessingTimeTimer")
- 启动 Flink 任务。
背景知识拓展
1. KeyedProcessFunction
- 作用:用于处理 keyed 数据流,支持状态管理和定时器。
- 核心方法:
processElement
:处理每条数据。onTimer
:定时器触发时执行的操作。
2. 处理时间(Processing Time)
- 定义:数据被处理时的系统时间。
- 特点:简单、高效,但无法处理乱序事件。
- 应用场景:对实时性要求高,但对事件顺序不敏感的场景。
3. 事件时间(Event Time)
- 定义:数据实际发生的时间。
- 特点:能处理乱序事件,但需要水位线(Watermark)机制。
- 应用场景:对事件顺序敏感的场景,如日志分析、交易监控。
4. 水位线(Watermark)
- 作用:用于处理乱序事件,标记事件时间的进度。
- 生成方式:通常基于事件时间戳生成。
- 延迟处理:允许一定时间范围内的延迟数据。
5. 定时器
- 类型:
- 处理时间定时器:基于系统时间触发。
- 事件时间定时器:基于事件时间触发。
- 注册方式:通过
context.timerService().registerProcessingTimeTimer
或context.timerService().registerEventTimeTimer
注册。
6. Flink 的时间语义
- 处理时间(Processing Time):数据被处理时的系统时间。
- 事件时间(Event Time):数据实际发生的时间。
- 摄入时间(Ingestion Time):数据进入 Flink 系统的时间。
7. Flink 的状态管理
- Keyed State:与 key 绑定的状态,如
ValueState
、ListState
。 - Operator State:与算子绑定的状态,如
ListState
、BroadcastState
。 - 状态后端(State Backend):用于存储状态,如
MemoryStateBackend
、RocksDBStateBackend
。
8. Flink 的容错机制
- Checkpoint:定期保存状态,用于故障恢复。
- Savepoint:手动触发的状态保存,用于版本升级或任务迁移。
进一步学习
- Flink 官方文档
https://flink.apache.org/docs/stable/
- KeyedProcessFunction 教程
https://ci.apache.org/projects/flink/flink-docs-stable/dev/stream/operators/process_function.html
- 时间语义与水位线:了解 Flink 的时间处理机制。
- 状态管理与容错:学习 Flink 的状态管理和容错机制。
- Scala 编程
https://docs.scala-lang.org/
通过这段代码的学习,你可以掌握如何使用 Flink 的 KeyedProcessFunction
实现处理时间和事件时间的定时器,并了解 Flink 的时间语义、水位线机制以及状态管理等核心概念。