一、概念
Timer(定时器)是Flink Streaming API提供的用于感知并利用处理时间/事件时间变化的机制。
最常见的使用Timer的地方就是KeyedProcessFunction。我们在其processElement()方法中注册Timer,然后覆写其onTimer()方法作为Timer触发时的回调逻辑。根据时间特征的不同:
(1)处理时间——调用Context.timerService().registerProcessingTimeTimer()注册;onTimer()在系统时间戳达到Timer设定的时间戳时触发。
(2)事件时间——调用Context.timerService().registerEventTimeTimer()注册;onTimer()在Flink内部水印达到或超过Timer设定的时间戳时触发。
(1) 用途
Flink定时器存在于窗口的触发,TTL等诸多用途,因此搞清楚其原理对于理解这些知识点至关重要。
(2)原理
上图表示flink延时调用的总体流程,其设计也是借助于优先级队列(小顶堆)来完成,堆使用二叉树实现,而二叉树使用数组存储
(3) 注册
ProcessingTime类型注册使用registerProcessingTimeTimer,传入的是一个触发的时间戳,内部会将获取到当前的Key、VoidNamespace 、timestamp封装成为一个InternalTimer对象存入优先级队列(小顶堆)中。并且会针对堆顶元素,使用ScheduledThreadPoolExecutor注册一个堆顶元素触发时间与当前时间差值大小的延时调用;
EventTime类型注册使用registerEventTimeTimer,与ProcessingTime类型注册不同的是不需要做延时调用,并且二者使用的是不同的队列
(4)延迟队列state
为了保证任务重启仍然能够执行未完成的延时调用,flink会在checkpoint过程中将优先级队列中的数据一起持久化到hdfs上,待下次任务重启仍然能够获取到这部分数据。由于EventTime类型定时器是由Watermark,那么只要任务产生watermark就能正常触发恢复的定时任务,但是ProcessingTime类型的定时器是由系统注册的延时调度来触发,所以在重启的时候获取到队列中第一个元素来注册延时调度,保证其恢复之后的正常触发。
(5)定时器注意事项
优先级队列默认使用的是内存存储,在一些数据量比较大并且重度依赖定时触发的任务会占用比较大的内存,可以选择Rocksdb存储定时信息
flink为了保证定时触发操作(onTimer)与正常处理(processElement)操作的线程安全,做了同步处理,在调用触发时必须要获取到锁,也就是二者同时只能有一个执行,因此一定要保证onTimer处理的速度,以免任务发生阻塞。
版权归原作者 m0_66520412 所有, 如有侵权,请联系我们删除。