flink的window理解

概述

​ 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();
		}
	}
發佈了23 篇原創文章 · 獲贊 7 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章