flink的window理解

本文深入探讨了Flink中Window机制的运作原理,包括TimeWindow和CountWindow的使用,以及如何通过keyBy操作对流数据进行分组。此外,还详细介绍了WindowAssigner、Trigger和Evictor的作用和实现细节,帮助读者全面理解Flink流处理的核心概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

​ window可以将flink处理的无限stream流切分成有限流,进行时间段内数据的计算,它是有限流处理的核心组件。window对流的切分可以是基于时间的(Time Window),也可以是基于数据的(Count Window)。

主要的操作如下:

注:例子中的kafkaSource是一个DataStream对象

keyed windows operator

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oI325lYA-1574937195558)(/Users/jiang.li/Library/Application Support/typora-user-images/image-20191127095351226.png)]

Non-keyed windows operator

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cLeVrcR7-1574937195566)(/Users/jiang.li/Library/Application Support/typora-user-images/image-20191127095457046.png)]

这里可以看到,除了没有进行keyBy操作以及之后的window操作外,其他的操作都是一样的。略作解释:keyBy操作之后生成的KeyedStream<String,Tuple> 是 DataStream的子类,keyedStream的属性和方法如下图:
在这里插入图片描述
DataStream的属性和方法如下图:
在这里插入图片描述

可以对比出,keyedStream扩展了关于window的方法,countWindow(countWindowAll),timeWindow(timeWindowAll),window(WindowAll),用来表示根据key(水平切分)和window(竖直切分)划分好的单元格。至于三个window之间的关系,window需要传入一个WindowAssigner来进行窗口的创建,countWindow是将WindowAssigner指定为GlobalWindows同时借助trigger和evictor操作来实现的,如下图
在这里插入图片描述
timeWindow是根据时间语义将WindowAssigner指定为TumblingProcessingTimeWindows/TumblingEventTimeWindows 或者 SlidingProcessingTimeWindows/SlidingEventTimeWindows来实现的
在这里插入图片描述
同时keyedStream重写了addSink,process,setConnectionType方法,这里就不展开了。

其他的这里也暂时不展开。

关于windowAssigner

具体windowAssigner的子类见下图
在这里插入图片描述
它负责将每条输入数据分发到正确的window中。Flink 提供了几种通用的 WindowAssigner:tumbling window(窗口间的元素无重复),sliding window(窗口间的元素可能重复),session window 以及 global window。如果需要自己定制数据分发策略,则可以实现一个 class,继承自 WindowAssigner。

​ tumbling window(窗口间的元素无重复)

在这里插入图片描述

​ sliding window(窗口间的元素可能重复)

在这里插入图片描述

​ Session Window(一个会话一个window)

​ Global Window(全部数据都在一个window)

关于evictor

​ evictor 主要用于做一些数据的自定义操作,可以在执行用户代码之前(evicBefore方法),也可以在执行用户代码之后(evicAfter方法),是可选的方法,如果用户不选择,则默认没有。具体的继承关系如下图

image-20191128085615246

* CountEvictor 保留指定数量的元素,需要指定是用户代码执行之前保留还是执行之后保留

* DeltaEvictor 通过执行用户给定的 DeltaFunction 以及预设的 threshold,判断是否删除一个元素。

* TimeEvictor设定一个阈值 interval,删除所有不再 max_ts – interval 范围内的元素,其中 max_ts 是窗口内时间戳的最大值。

关于trigger

trigger 用来判断一个窗口是否需要被触发,每个 WindowAssigner 都自带一个默认的 trigger,trigger继承关系如下:
在这里插入图片描述

如果默认的 trigger 不能满足你的需求,则可以自定义一个类,继承自 Trigger 即可,我们详细描述下 Trigger 的接口以及含义:

  • onElement() 每次往 window 增加一个元素的时候都会触发

* onEventTime() 当 event-time timer 被触发的时候会调用

* onProcessingTime() 当 processing-time timer 被触发的时候会调用

* onMerge() 对两个 trigger 的 state 进行 merge 操作

* clear() window 销毁的时候被调用

上面的接口中前三个会返回一个 TriggerResult,TriggerResult 有如下几种可能的选择:

* CONTINUE 不做任何事情

* FIRE 触发 window

* PURGE 清空整个 window 的元素并销毁窗口

* FIRE_AND_PURGE 触发窗口,然后销毁窗口

这里以org.apache.flink.streaming.api.windowing.triggers.ContinuousEventTimeTrigger为例进行剖析:

首先在创建对象的时候

	//触发的间隔时间
	private final long interval;
	//创建状态变量描述
	/** When merging we take the lowest of all fire timestamps as the new fire timestamp. */
	private final ReducingStateDescriptor<Long> stateDesc =
			new ReducingStateDescriptor<>("fire-time", new Min(), LongSerializer.INSTANCE);
	//构造方法
	private ContinuousEventTimeTrigger(long interval) {
		this.interval = interval;
	}

刚开始向window中增加元素的时候会触发onElement方法

	//每次往 window 增加一个元素的时候都会触发,但是不做任何事情
	@Override
	public TriggerResult onElement(Object element, long timestamp, W 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());
		}
		//获取当前分区的状态
		ReducingState<Long> fireTimestamp = ctx.getPartitionedState(stateDesc);
    //判断状态中的值
		if (fireTimestamp.get() == null) {
			//上次触发时间/开始放入元素的时间
			long start = timestamp - (timestamp % interval);
			//下次启动时间
			long nextFireTimestamp = start + interval;
			//注册定时器
			ctx.registerEventTimeTimer(nextFireTimestamp);
			//添加下次启动时间,不停的添加会导致存在多个触发时间,之后会进行merge操作
			fireTimestamp.add(nextFireTimestamp);
		}

		return TriggerResult.CONTINUE;
	}

当 event-time timer 被触发的时候会调用

	//当 event-time timer 被触发的时候会调用
	//time表示当前时间
	@Override
	public TriggerResult onEventTime(long time, W window, TriggerContext ctx) throws Exception {

		if (time == window.maxTimestamp()){
			//如果最大时间到了就会触发整个window,且不回进行下次触发
			return TriggerResult.FIRE;
		}

		ReducingState<Long> fireTimestampState = ctx.getPartitionedState(stateDesc);

		Long fireTimestamp = fireTimestampState.get();

		if (fireTimestamp != null && fireTimestamp == time) {
			//如果fireTimestamp不为空且是当前时间,那么触发window
      //清理当前状态
			fireTimestampState.clear();
			fireTimestampState.add(time + interval);
			//注册定时启动器,下次调用此方式的时间
			ctx.registerEventTimeTimer(time + interval);
			return TriggerResult.FIRE;
		}

		return TriggerResult.CONTINUE;
	}

通过同时存在好多触发器,会进行以下操作

	@Override
	public boolean canMerge() {
		return true;
	}

	//对多个 trigger 的 state 进行 merge 操作
	@Override
	public void onMerge(W window, OnMergeContext ctx) throws Exception {
		//将当前分区的state进行merge操作
		ctx.mergePartitionedState(stateDesc);
		//获取下次触发的时间
		Long nextFireTimestamp = ctx.getPartitionedState(stateDesc).get();
		if (nextFireTimestamp != null) {
			ctx.registerEventTimeTimer(nextFireTimestamp);
		}
	}

window 销毁的时候被调用

	//window 销毁的时候被调用
	@Override
	public void clear(W window, TriggerContext ctx) throws Exception {
		ReducingState<Long> fireTimestamp = ctx.getPartitionedState(stateDesc);
		Long timestamp = fireTimestamp.get();
		if (timestamp != null) {
			ctx.deleteEventTimeTimer(timestamp);
			fireTimestamp.clear();
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值