Flink – process watermark
WindowOperator.processElement
主要的工作,将当前的element的value加到对应的window中,
windowState.setCurrentNamespace(window);
windowState.add(element.getValue()); triggerContext.key = key;
triggerContext.window = window; TriggerResult triggerResult = triggerContext.onElement(element);
调用triggerContext.onElement
这里的Context只是一个简单的封装,
public TriggerResult onElement(StreamRecord<IN> element) throws Exception {
return trigger.onElement(element.getValue(), element.getTimestamp(), window, this);
}
EventTimeTrigger
onElement
@Override
public TriggerResult onElement(Object element, long timestamp, TimeWindow window, TriggerContext ctx) throws Exception {
if (window.maxTimestamp() <= ctx.getCurrentWatermark()) {
// if the watermark is already past the window fire immediately
return TriggerResult.FIRE;
} else {
ctx.registerEventTimeTimer(window.maxTimestamp());
return TriggerResult.CONTINUE;
}
}
如果当前window.maxTimestamp已经小于CurrentWatermark,直接触发
否则将window.maxTimestamp注册到TimeService中,等待触发
WindowOperator.Context
public void registerEventTimeTimer(long time) {
internalTimerService.registerEventTimeTimer(window, time);
}
InternalTimerService
在AbstractStreamOperator
public abstract class AbstractStreamOperator<OUT>
implements StreamOperator<OUT>, Serializable, KeyContext {
注意这里实现了KeyContext
所以AbstractStreamOperator实现了
public Object getCurrentKey() {
if (keyedStateBackend != null) {
return keyedStateBackend.getCurrentKey();
} else {
throw new UnsupportedOperationException("Key can only be retrieven on KeyedStream.");
}
}
在AbstractStreamOperator初始化InternalTimeServiceManager
private transient InternalTimeServiceManager<?, ?> timeServiceManager;
@Override
public final void initializeState(OperatorStateHandles stateHandles) throws Exception { if (getKeyedStateBackend() != null && timeServiceManager == null) {
timeServiceManager = new InternalTimeServiceManager<>(
getKeyedStateBackend().getNumberOfKeyGroups(),
getKeyedStateBackend().getKeyGroupRange(),
this,
getRuntimeContext().getProcessingTimeService());
}
WindowOperator中InternalTimerService初始化,
internalTimerService =
getInternalTimerService("window-timers", windowSerializer, this);
在AbstractStreamOperator调用,
public <K, N> InternalTimerService<N> getInternalTimerService(
String name,
TypeSerializer<N> namespaceSerializer,
Triggerable<K, N> triggerable) { checkTimerServiceInitialization(); // the following casting is to overcome type restrictions.
TypeSerializer<K> keySerializer = (TypeSerializer<K>) getKeyedStateBackend().getKeySerializer();
InternalTimeServiceManager<K, N> keyedTimeServiceHandler = (InternalTimeServiceManager<K, N>) timeServiceManager;
return keyedTimeServiceHandler.getInternalTimerService(name, keySerializer, namespaceSerializer, triggerable);
}
其实就是调用InternalTimeServiceManager.getInternalTimerService
最终得到HeapInternalTimerService
HeapInternalTimerService.registerEventTimeTimer
@Override
public void registerEventTimeTimer(N namespace, long time) {
InternalTimer<K, N> timer = new InternalTimer<>(time, (K) keyContext.getCurrentKey(), namespace);
Set<InternalTimer<K, N>> timerSet = getEventTimeTimerSetForTimer(timer);
if (timerSet.add(timer)) {
eventTimeTimersQueue.add(timer);
}
}
创建InternalTimer,包含,time(window.maxTimestamp), key(keyContext.getCurrentKey), namespace(window)
getEventTimeTimerSetForTimer
private Set<InternalTimer<K, N>> getEventTimeTimerSetForTimer(InternalTimer<K, N> timer) {
checkArgument(localKeyGroupRange != null, "The operator has not been initialized.");
int keyGroupIdx = KeyGroupRangeAssignment.assignToKeyGroup(timer.getKey(), this.totalKeyGroups);
return getEventTimeTimerSetForKeyGroup(keyGroupIdx);
}
private Set<InternalTimer<K, N>> getEventTimeTimerSetForKeyGroup(int keyGroupIdx) {
int localIdx = getIndexForKeyGroup(keyGroupIdx);
Set<InternalTimer<K, N>> timers = eventTimeTimersByKeyGroup[localIdx];
if (timers == null) {
timers = new HashSet<>();
eventTimeTimersByKeyGroup[localIdx] = timers;
}
return timers;
}
先找到key所对应的,keygroup,每个keygroup对应于一个Timer集合
这样设计的目的,因为最终timer也是要checkpoint的,而checkpoint的最小单位是keygroup,所以不同keygroup所对应的timer需要分离开
最终把timer加到eventTimeTimersQueue,
private final PriorityQueue<InternalTimer<K, N>> eventTimeTimersQueue;
PriorityQueue是堆实现的,所以只要在InternalTimer里面实现compareTo,就可以让timer排序
AbstractStreamOperator.processWatermark
public void processWatermark(Watermark mark) throws Exception {
if (timeServiceManager != null) {
timeServiceManager.advanceWatermark(mark);
}
output.emitWatermark(mark);
}
timeServiceManager.advanceWatermark
public void advanceWatermark(Watermark watermark) throws Exception {
for (HeapInternalTimerService<?, ?> service : timerServices.values()) {
service.advanceWatermark(watermark.getTimestamp());
}
}
HeapInternalTimerService.advanceWatermark
public void advanceWatermark(long time) throws Exception {
currentWatermark = time; InternalTimer<K, N> timer; while ((timer = eventTimeTimersQueue.peek()) != null && timer.getTimestamp() <= time) { Set<InternalTimer<K, N>> timerSet = getEventTimeTimerSetForTimer(timer);
timerSet.remove(timer);
eventTimeTimersQueue.remove(); keyContext.setCurrentKey(timer.getKey());
triggerTarget.onEventTime(timer);
}
}
从eventTimeTimersQueue从小到大取timer,如果小于传入的water mark,那么说明这个window需要触发
设置operater的current key,keyContext.setCurrentKey(timer.getKey())
这里注意watermarker是没有key的,所以当一个watermark来的时候是会触发所有timer,而timer的key是不一定的,所以这里一定要设置keyContext,否则就乱了
最终触发triggerTarget.onEventTime
triggerTarget就是WindowOperator
WindowOperator.onEventTime
windowState.setCurrentNamespace(triggerContext.window); ACC contents = null;
if (windowState != null) {
contents = windowState.get();
} if (contents != null) {
TriggerResult triggerResult = triggerContext.onEventTime(timer.getTimestamp());
if (triggerResult.isFire()) {
emitWindowContents(triggerContext.window, contents);
}
if (triggerResult.isPurge()) {
windowState.clear();
}
}
这里调用triggerContext.onEventTime,得到TriggerResult
如果fire,走到这,这个肯定满足的,emitWindowContents
如果purge,就把windowState清空
emitWindowContents,调用用户定义的windowFunction来处理window的contents
private void emitWindowContents(W window, ACC contents) throws Exception {
timestampedCollector.setAbsoluteTimestamp(window.maxTimestamp());
processContext.window = window;
userFunction.process(triggerContext.key, window, processContext, contents, timestampedCollector);
}
Flink – process watermark的更多相关文章
- flink的watermark机制你学会了吗?
大家好,今天我们来聊一聊flink的Watermark机制. 这也是flink系列的的第一篇文章,如果对flink.大数据感兴趣的小伙伴,记得点个关注呀. 背景 flink作为先进的流水计算引擎, ...
- [白话解析] Flink的Watermark机制
[白话解析] Flink的Watermark机制 0x00 摘要 对于Flink来说,Watermark是个很难绕过去的概念.本文将从整体的思路上来说,运用感性直觉的思考来帮大家梳理Watermark ...
- [Flink] Flink的waterMark的通俗理解
导读 Flink 为实时计算提供了三种时间,即事件时间(event time).摄入时间(ingestion time)和处理时间(processing time). 遇到的问题: 假设在一个5秒的T ...
- Flink中watermark为什么选择最小一条(源码分析)
昨天在社区群看到有人问,为什么水印取最小的一条?这里分享一下自己的理解 首先水印一般是设置为:(事件时间 - 指定的值) 这里的作用是解决迟到数据的问题,从源码来看一下它如何解决的 先来看下wind ...
- [源码分析] 从源码入手看 Flink Watermark 之传播过程
[源码分析] 从源码入手看 Flink Watermark 之传播过程 0x00 摘要 本文将通过源码分析,带领大家熟悉Flink Watermark 之传播过程,顺便也可以对Flink整体逻辑有一个 ...
- 老板让阿粉学习 flink 中的 Watermark,现在他出教程了
1 前言 在时间 Time 那一篇中,介绍了三种时间概念 Event.Ingestin 和 Process, 其中还简单介绍了乱序 Event Time 事件和它的解决方案 Watermark 水位线 ...
- Flink Program Guide (5) -- 预定义的Timestamp Extractor / Watermark Emitter (DataStream API编程指导 -- For Java)
本文翻译自Pre-defined Timestamp Extractors / Watermark Emitter ------------------------------------------ ...
- Flink DataStream API Programming Guide
Example Program The following program is a complete, working example of streaming window word count ...
- <译>Flink编程指南
Flink 的流数据 API 编程指南 Flink 的流数据处理程序是常规的程序 ,通过再流数据上,实现了各种转换 (比如 过滤, 更新中间状态, 定义窗口, 聚合).流数据可以来之多种数据源 (比如 ...
随机推荐
- 物联网架构成长之路(23)-Docker练习之Elasticsearch服务搭建
0. 前言 最近基本都是学一些环境配置,和一些中间件的安装与配置.没有实际编写代码.可能看起来有点水,我对自己的学习方式是,先要了解各个中间件的安装配置以及简单使用,理论应用场景,然后我在小项目中,逐 ...
- 使用SharedPreference和对象流存储对象
编写PreferencesUtils工具类可以简单对象(可以缓存对象中所有public的简单属性) 另外研究了Preference存储与直接采用对象文件缓存方式的区别 第一次写文件时 1.效率,直 ...
- Centos7.x 执行top命令教程
一.作用 top命令用来显示执行中的进程,使用权限是所有用户 二.格式 top [-] [d delay] [q] [c] [S] [s] [i] [n] 三.命令参数解释 d:指定更新的间隔,以秒计 ...
- 【Unity】Protobuf的使用与常见问题
Protobuf的使用流程 protobuf参考教程:https://www.jianshu.com/p/b135676dbe8d 手写.proto文件后,用CMD命令行运行protoc.exe编译器 ...
- 教你一招:修复win7 系统自带的截图工具损坏
这个问题经常见,原因是注册表没有导入. 修复很简单. 打开资源管理器,在C盘中搜索到 tpcps.dll ,在其中选一个右击,选择注册dll,然后截图工具就被修复了. 有时候便签也会出现类似问题,方法 ...
- ORA-00257:archiver error问题处理方法
原文链接:http://www.7747.net/database/201109/104615.html ORA-00257: archiver error. Connect internal onl ...
- Springboot学习笔记(五)-条件化注入
前言 将Bean交给spring托管很简单,根据功能在类上添加@Component,@Service,@Controller等等都行,如果是第三方类,也可以通过标有@Configuration的配置类 ...
- [转]搞个这样的 APP 要多久
我有些尴尬地拿着水杯,正对面坐着来访的王总,他是在别处打拼的人,这几年据说收获颇丰,见移动互联网如火如荼,自然也想着要进来干一场,尽管王总从事的行当也算跟IT沾边,但毕竟太长时间不接触技术,有些东西不 ...
- python -u 启动python文件的作用,PYTHONUNBUFFERED环境变量的作用
python -u 启动python文件的作用是不缓存,直接把输出重定向到文件,比如nohup启动什么的,如果不使用-u启动,那么程序中的print和日志什么的,可能不会非常及时的重定向到out文件, ...
- [SLAM] 03. ORB-SLAM2
一年后再读SLAM~ 行业有了不少工程实践方面的突破 一.链接:https://www.zhihu.com/question/53571648/answer/176732257 目前来说,受到业界肯定 ...