目前有許多數據分析的場景從批處理到流處理的演變, 雖然能夠將批處理做爲流處理的特殊狀況來處理,可是分析無窮集的流數據一般須要思惟方式的轉變而且具備其本身的術語(例如,「windowing(窗口化)」、「at-least-once(至少一次)」、「exactly-once(只有一次)」 )。html
對於剛剛接觸流處理的人來講,這種轉變和新術語可能會很是混亂。 Apache Flink 是一個爲生產環境而生的流處理器,具備易於使用的 API,能夠用於定義高級流分析程序。apache
Flink 的 API 在數據流上具備很是靈活的窗口定義,使其在其餘開源流處理框架中脫穎而出。windows
在這篇文章中,咱們將討論用於流處理的窗口的概念,介紹 Flink 的內置窗口,並解釋它對自定義窗口語義的支持。session
下面咱們結合一個現實的例子來講明。框架
就拿交通傳感器的示例:統計通過某紅綠燈的汽車數量之和?函數
假設在一個紅綠燈處,咱們每隔 15 秒統計一次經過此紅綠燈的汽車數量,以下圖:學習
能夠把汽車的通過當作一個流,無窮的流,不斷有汽車通過此紅綠燈,所以沒法統計總共的汽車數量。可是,咱們能夠換一種思路,每隔 15 秒,咱們都將與上一次的結果進行 sum 操做(滑動聚合),以下:spa
這個結果彷佛仍是沒法回答咱們的問題,根本緣由在於流是無界的,咱們不能限制流,但能夠在有一個有界的範圍內處理無界的流數據。.net
所以,咱們須要換一個問題的提法:每分鐘通過某紅綠燈的汽車數量之和?
這個問題,就至關於一個定義了一個 Window(窗口),window 的界限是1分鐘,且每分鐘內的數據互不干擾,所以也能夠稱爲翻滾(不重合)窗口,以下圖:htm
第一分鐘的數量爲8,第二分鐘是22,第三分鐘是27。。。這樣,1個小時內會有60個window。
再考慮一種狀況,每30秒統計一次過去1分鐘的汽車數量之和:
此時,window 出現了重合。這樣,1個小時內會有120個 window。
擴展一下,咱們能夠在某個地區,收集每個紅綠燈處汽車通過的數量,而後每一個紅綠燈處都作一次基於1分鐘的window統計,即並行處理:
一般來說,Window 就是用來對一個無限的流設置一個有限的集合,在有界的數據集上進行操做的一種機制。window 又能夠分爲基於時間(Time-based)的 window 以及基於數量(Count-based)的 window。
Flink DataStream API 提供了 Time 和 Count 的 window,同時增長了基於 Session 的 window。同時,因爲某些特殊的須要,DataStream API 也提供了定製化的 window 操做,供用戶自定義 window。
下面,主要介紹 Time-Based window 以及 Count-Based window,以及自定義的 window 操做,Session-Based Window 操做將會在後續的文章中講到。
正如命名那樣,Time Windows 根據時間來聚合流數據。例如:一分鐘的 tumbling time window 收集一分鐘的元素,並在一分鐘事後對窗口中的全部元素應用於一個函數。
在 Flink 中定義 tumbling time windows(翻滾時間窗口) 和 sliding time windows(滑動時間窗口) 很是簡單:
tumbling time windows(翻滾時間窗口)
輸入一個時間參數
1 2 3 |
data.keyBy(1) .timeWindow(Time.minutes(1)) //tumbling time window 每分鐘統計一次數量和 .sum(1); |
sliding time windows(滑動時間窗口)
輸入兩個時間參數
1 2 3 |
data.keyBy(1) .timeWindow(Time.minutes(1), Time.seconds(30)) //sliding time window 每隔 30s 統計過去一分鐘的數量和 .sum(1); |
有一點咱們尚未討論,即「收集一分鐘的元素」的確切含義,它能夠歸結爲一個問題,「流處理器如何解釋時間?」
Apache Flink 具備三個不一樣的時間概念,即 processing time, event time 和 ingestion time。
這裏能夠參考我下一篇文章:
《從0到1學習Flink》—— 介紹Flink中的Event Time、Processing Time和Ingestion Time
Apache Flink 還提供計數窗口功能。若是計數窗口設置的爲 100 ,那麼將會在窗口中收集 100 個事件,並在添加第 100 個元素時計算窗口的值。
在 Flink 的 DataStream API 中,tumbling count window 和 sliding count window 的定義以下:
tumbling count window
輸入一個時間參數
1 2 3 |
data.keyBy(1) .countWindow(100) //統計每 100 個元素的數量之和 .sum(1); |
sliding count window
輸入兩個時間參數
1 2 3 |
data.keyBy(1) .countWindow(100, 10) //每 10 個元素統計過去 100 個元素的數量之和 .sum(1); |
Flink 的內置 time window 和 count window 已經覆蓋了大多數應用場景,可是有時候也須要定製窗口邏輯,此時 Flink 的內置的 window 沒法解決這些問題。爲了還支持自定義 window 實現不一樣的邏輯,DataStream API 爲其窗口機制提供了接口。
下圖描述了 Flink 的窗口機制,並介紹了所涉及的組件:
到達窗口操做符的元素被傳遞給 WindowAssigner。WindowAssigner 將元素分配給一個或多個窗口,可能會建立新的窗口。
窗口自己只是元素列表的標識符,它可能提供一些可選的元信息,例如 TimeWindow 中的開始和結束時間。注意,元素能夠被添加到多個窗口,這也意味着一個元素能夠同時在多個窗口存在。
每一個窗口都擁有一個 Trigger(觸發器),該 Trigger(觸發器) 決定什麼時候計算和清除窗口。當先前註冊的計時器超時時,將爲插入窗口的每一個元素調用觸發器。在每一個事件上,觸發器均可以決定觸發(即、清除(刪除窗口並丟棄其內容),或者啓動並清除窗口。一個窗口能夠被求值屢次,而且在被清除以前一直存在。注意,在清除窗口以前,窗口將一直消耗內存。
當 Trigger(觸發器) 觸發時,能夠將窗口元素列表提供給可選的 Evictor,Evictor 能夠遍歷窗口元素列表,並能夠決定從列表的開頭刪除首先進入窗口的一些元素。而後其他的元素被賦給一個計算函數,若是沒有定義 Evictor,觸發器直接將全部窗口元素交給計算函數。
計算函數接收 Evictor 過濾後的窗口元素,並計算窗口的一個或多個元素的結果。 DataStream API 接受不一樣類型的計算函數,包括預約義的聚合函數,如 sum(),min(),max(),以及 ReduceFunction,FoldFunction 或 WindowFunction。
這些是構成 Flink 窗口機制的組件。 接下來咱們逐步演示如何使用 DataStream API 實現自定義窗口邏輯。 咱們從 DataStream [IN] 類型的流開始,並使用 key 選擇器函數對其分組,該函數將 key 相同類型的數據分組在一塊。
1 2 |
SingleOutputStreamOperator<xxx> data = env.addSource(...); data.keyBy() |
負責將元素分配到不一樣的 window。
Window API 提供了自定義的 WindowAssigner 接口,咱們能夠實現 WindowAssigner 的
1 |
public abstract Collection<W> assignWindows(T element, long timestamp) |
方法。同時,對於基於 Count 的 window 而言,默認採用了 GlobalWindow 的 window assigner,例如:
1 |
keyBy.window(GlobalWindows.create()) |
Trigger 即觸發器,定義什麼時候或什麼狀況下移除 window
咱們能夠指定觸發器來覆蓋 WindowAssigner 提供的默認觸發器。 請注意,指定的觸發器不會添加其餘觸發條件,但會替換當前觸發器。
驅逐者,即保留上一 window 留下的某些元素
利用 Flink 的內部窗口機制和 DataStream API 能夠實現自定義的窗口邏輯,例如 session window。
對於現代流處理器來講,支持連續數據流上的各類類型的窗口是必不可少的。 Apache Flink 是一個具備強大功能集的流處理器,包括一個很是靈活的機制,能夠在連續數據流上構建窗口。 Flink 爲常見場景提供內置的窗口運算符,以及容許用戶自定義窗口邏輯。
一、https://flink.apache.org/news/2015/12/04/Introducing-windows.html