Flink定时器实战:处理时间与事件时间

目录

代码解释

1. 环境设置

2. 基于处理时间的定时器

3. 基于事件时间的定时器

4. 自定义数据源

5. 任务执行

背景知识拓展

1. KeyedProcessFunction

2. 处理时间(Processing Time)

3. 事件时间(Event Time)

4. 水位线(Watermark)

5. 定时器

6. Flink 的时间语义

7. Flink 的状态管理

8. Flink 的容错机制

进一步学习


 

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 绑定的状态,如 ValueStateListState
  • Operator State:与算子绑定的状态,如 ListStateBroadcastState
  • 状态后端(State Backend):用于存储状态,如 MemoryStateBackendRocksDBStateBackend
8. Flink 的容错机制
  • Checkpoint:定期保存状态,用于故障恢复。
  • Savepoint:手动触发的状态保存,用于版本升级或任务迁移。

进一步学习

通过这段代码的学习,你可以掌握如何使用 Flink 的 KeyedProcessFunction 实现处理时间和事件时间的定时器,并了解 Flink 的时间语义、水位线机制以及状态管理等核心概念。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

渣渣盟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值