[toc]
本文主要介绍flink中TimerService based on Rocksdb实现以及和之前版本的一个比较。
动机
- timer和keyed state分开单独管理,keyed state是由
KeyedStateBackend管理,而timer是由InternalTimerServeice管理 InternalTimerServeice现有的实现是基于heap的HeapInternalTimerService,当timer数量较多时会有OOM的问题.如果能像keyed state一样基于KeyedStateBackend管理,就能在timer数量比较多的时候选用rocksdb作为backend来解决扩展性的问题- timer目前的checkpoint过程是通过raw keyed state的方式,在同步的过程中完成写出到外置存储,并且对于snapshot和restore timer都单独维护了一份代码。这块代码和其他的keyed state的实现有很多相同之处(分隔到keygroup来实现rescale,元数据的序列化和持久化..)
实现目标
- Have an implementation of timer services that operates on RocksDB.
- Support asynchronous snapshots for all timer state.
- Support incremental snapshots for timer state in RocksDB.
- Integrate timer state as another form of keyed state in keyed state backends in a way that leverages the existing snapshotting code to eliminate special casing code paths that do similar things. As as nice side effect, this would also free the raw keyed state for user state.
源码分析
首先我们来看一下1.4版本中timerService是怎么实现的,timerService 实现了以下两个接口:
1 | InternalTimerService |
在HeapInternalTimerService中分别维护了processing time和event time的timer集合
1 | private final Set<InternalTimer<K, N>>[] processingTimeTimersByKeyGroup; |
InternalTimer是一个Comparable, 按照timer触发的时间进行比较,timer是由key,namespace,timestamp唯一确定,其实也可以理解成如果同一个key,namespace下只可以有一个时间事件被触发。regsterTimer实际上就是在executor中注册一个任务
1 | return timerService.schedule( |
同时每个timerService中还维护了一个ProcessingTimeService用以处理和processing time相关的时间操作,是对ScheduledThreadPoolExecutor的包装,提供一些周期性执行和将来某时执行一次的操作.
我们在回头看HeapInternalTimerService的实现:
registerProcessingTimeTimer
1 | public void registerProcessingTimeTimer(N namespace, long time) { |
registerEventTimeTimer
1 | public void registerEventTimeTimer(N namespace, long time) { |
可以看到eventtimer注册就不需要校验是否有将要执行的任务,因为eventtimer的实现不依赖于schedulerExxecutor。
onProcessingTime
1 | // 这个方法是配合registerProcessingTimer,通过SystemProcessingTimeService来实现timer语义,在timer中注册的任务会回调这个onProcessing方法 |
advanceWatermark
processing timer是基于executor来实现的,eventtime 的timer触发就依赖于watermark来触发,每次收到上游的watermark会触发调用advanceWatermark来将eventtime queue中的timer取出进行触发
1 | public void processWatermark(Watermark mark) throws Exception { |
1 | // 这里的time就是最近的这次watermark的时间 |
snapshot
之前在分析state实现的时候也分析过,在对operator进行snapshot的时候有一步就是对timerservice的数据进行snapshot
1 | KeyGroupsList allKeyGroups = out.getKeyGroupList(); |
proxy这块主要是为兼容做了很多的工作
1 | public void write(DataOutputView out) throws IOException { |
restore
1 | protected void read(DataInputView in, boolean wasVersioned) throws IOException { |
本文主要是对1.4版本的分析,下一篇文章基于1.7版本再分析timerservice on rocksdb的实现
参考: