回顧2016--Apache Flink流處理在生產中的實踐

從2016年4月底開始接觸Flink,到現在已經8個多月了。從瞭解到熟悉,再到實際開發,這個過程就是我從0到實際開發使用Flink的過程。

上週,我們的Flink流計算程序終於上線了。也算是在實時流計算方面的一個成果。

下面,我將簡要介紹下公司如何使用Flink進行流處理的開發。

1、簡介

我們主要是針對股票市場,進行實時的流處理,統計出各種相關的指標,並將輸出提供給公司的產品用作展示和一些策略的生成。

公司要求提供的數據以低延遲爲首要目標,同時儘可能保證正確性與較低的成本。我們的需求類似於下圖中的描述:

這裏寫圖片描述

在選型階段,開源的分佈式流處理的框架主要有Storm、Spark Streaming以及Flink等。

1、Spark Streaming: 當時正值Spark非常火爆的時期,但是其micro-batch的本質決定了其提供低延遲的能力相對較弱,不能滿足我們的需求,故放棄了Spark。

2、Storm: Storm可是說是低延遲的代表,但是其本身在吞吐量以及不能很好的提供exactly-once的處理(trident又迴歸批處理了),同時對於session window和state的支持都不夠好,因此也沒有使用。

3、Flink: 一個非常新(僅聽說過)的純流式計算框架,能夠提供低延遲、帶狀態的操作、提供基於event time的處理,同時支持session window以及處理亂序的能力,使得我們最終選擇了Flink。
  • 1
  • 2
  • 3
  • 4
  • 5

Flink特性:

這裏寫圖片描述

2、面臨的挑戰

選擇Flink,當時來講不得不說是一次大膽的嘗試,對於Google Cloud DataFlow,Flink算是對其論文實現最好的框架了,但是也是資料最少的一個框架。(當時都不知道國內有哪些公司在用,中文資料更是很少)

學習之初,只有官方文檔和源代碼,中文的資料當時除了2篇概括性的文章外(深入理解Apache Flink核心技術新一代大數據處理引擎 Apache Flink),就只有VinoYang’sBlog以及Jark’s Blog可以參考。

在學習的過程中,也非常感謝VinoYang和Jark以及李呈祥對我的幫助和對我提出的問題的耐心回答,使得我在最開始時度過了一段比較黑暗的時光。

3、Flink的部署模式

我們目前線上生產系統採用的Standalone HA模式,而測試環境則是yarn cluster模式。之所以線上沒有使用yarn模式,主要考慮是當前Flink中yarn的支持沒有實現動態分配,而且其他公司使用的也不多,而阿里的Blink中對yarn的改進,反饋給社區要到未來的版本,所以我們就穩妥起見,選擇了standalone模式。

4、data flow流程

下面進入重點,簡單介紹下我們如何使用Flink進行流處理開發的。

這裏寫圖片描述

我們的數據流大致如下:

這裏寫圖片描述

從交易所出來的數據,經過ZMQ接收並pub出來,給不同的框架或者其他系統統一使用。然後我們選擇Kafka作爲MQ,接收並存儲,然後立刻pub出來,供Flink消費。通過Flink處理後的數據流,根據某些需要會將部分結果再次寫回Kafka,再次由Flink處理;有些數據流在處理完成後,則是直接輸出到HDFS和DB中。

這裏有一個非常匪夷所思的架構設計就是ZeroMQ本身就是一個MQ框架,爲什麼還要用Kafka再加一層?這樣做豈不是既增加了延遲又增加了複雜度麼?

我們主要考慮到業務上要求對數據的處理更精細化,要做到exactly-once語義的處理。Flink的檢查點機制可以做到失敗時恢復,但源頭也必須很好的支持部分數據源的重發。這點很重要,而ZMQ不具備存儲數據的能力,消費完後就刪除了,所以我們只得使用Kafka來作爲Flink真正的源頭。

在實際的測試過程中,我們測試的結果是從數據產生(event time),到Flink消費時的時間(processing time)的間隔,平均在50ms左右。這達到了我們需求,所以加一層Kafka並沒有使得延遲增加,而且解決了我們的問題。

這裏寫圖片描述

5、streaming的細節

下圖展示了我們在流處理中的部分實現細節:

這裏寫圖片描述

5.1、單流與多流

1、單流的基於event time的窗口操作
2、雙流的基於event time的Join(實際上是coGroup)操作
  • 1
  • 2

Flink中的operator是可以有狀態的,這極大的降低了我們開發的難度,而且有效的實現了很多複雜計算的邏輯(apply中實現)。

同時,在單流(單流轉換)與多流中,我們有些也需要獲取外部數據源(DB)的配置信息,我們則通過RichXXXFunction中提供的Context功能,在open()中得以實現。

5.2、watermark

對於流數據在網絡傳輸中可能出現的亂序情況,我們通過watermark對亂序的數據進行處理。

由於我們採用event time作爲我們的時間度量,因此source之後,進行簡單的filter與map後,立刻emit watermark。我們這裏實現基於Periodic的Watermarks,並自定義與業務緊密相關的watermark機制。

這裏寫圖片描述

簡單來說還是以允許一段時間的亂序,加上一些邊界時,對watermark進行的特殊處理來實現自定義的watermark。

5.3、window

由於流是無界的,而我們需要將無界的流在一個有界的範圍內進行處理,因此在window內實現業務邏輯。

我們實際開發中,用到了2類窗口:

1、TimeWindow,這裏主要用到Flink中預定義的TumblingEventTimeWindow以及沒有區分key的windowAll操作。
2、GlobalWindow,這裏主要涉及基於count的窗口,通過數據的count來觸發窗口。
  • 1
  • 2

最開始本想採用session window實現,但是實際上需要兼容現有產品的一些設計,因此沒就沒有使用session window實現,轉而通過TimeWindow實現。

5.4、trigger

數據被聚合到window後,何時被觸發,這個就要依賴於trigger了。Flink中的trigger機制比較簡單,有3種抽象:

1、onElement
2、onProcessingTime
3、onEventTime
  • 1
  • 2
  • 3

由於我們只使用event time作爲處理的時間,以及使用GlobalWindow、基於count的觸發機制,因此我們都是使用了Flink預定義的watermark trigger機制,這也是默認的trigger機制,即當event time trigger時,watermark到達了window結束的時間後,自定觸發窗口計算;當count數量到達了設定的count值後,觸發窗口的計算。

Flink在trigger的實現上,目前還有很多改進之處,這在我們開發中也遇到了一些問題,例如由於嚴重依賴watermark trigger,因此如果長時間數據沒有到達窗口的觸發條件,則窗口不會觸發;這對於邊界時間來講,是不可接收的。

那我們是如何解決這個問題的呢?

我們通過了對watermark在邊界時間的改造,間接的實現了類似earlier trigger的功能,使得延遲保持較低的程度。
  • 1

其實現在想想,我們爲什麼要使用trigger?

trigger本質上來講的功能就是在數據的正確性、延遲以及成本之間進行平衡的一種機制。 
這裏寫圖片描述

只不過當前Flink1.1.X版本中,對trigger的實現還沒有像Google Cloud DataFlow那麼完備。

但現在Flink社區將在Flink1.2中,增加window中的元數據信息(例如提供觸發window的原因等),同時對trigger的支持將實現組合式trigger、earlier trigger以及later trigger等功能,同時,對於late的元素,也可以選擇丟棄或者重新累加計算等細化的功能。具體見:FLIP-2 Extending Window Function Metadata以及FLIP-9: Trigger DSL

這一特性將完全匹配DataFlow模型中的trigger機制,同時我們未來的程序中,也有可能使用最新的Flink版本進行開發。

5.5、sink

我們目前將中間的DataStream流寫入到Kafka、DB以及HDFS,供現有產品使用。

5.6、state存儲

Flink的檢查點的觸發,會生成分佈式快照,而快照中除了系統運行時的一些元數據信息外,就是程序中各種狀態的數據了,例如window中的數據,UDF的方法中的數據等。目的是在恢復時實現exactly-once語義的處理。

Flink支持有狀態的operator以及UDF的state,而支持的state backend包括3種方式:

1、JobManager memory(當前版本的默認模式)
2、HDFS
3、RocksDB(未來Flink版本的默認模式) 
  • 1
  • 2
  • 3

JobManager的memory存儲快照有很大的風險,主要表現在兩方面:

1、JobManager一旦失敗,則內存的快照數據丟失;如果配置了HA(通過zookeeper實現),雖然可以自動切換到standby JobManager,但是原JobManager中的快照信息就丟失了,也就無法做到exactly-once2、JobaManager的內存有限,一旦快照的大小很大,例如上百兆或者更大,那麼肯定會對JobManager造成性能上的影響;而且頻繁的將快照存入JobManager的內存,即使快照大小比較小,但是對於JobManager與TaskManager和Zookeeper之間的通信,也會造成一定的影響。
  • 1
  • 2
  • 3

基於這兩方面,我們選擇了較爲穩妥的HDFS上。這可以通過配置文件實現state backend的存儲;

對於RocksDB,則是官方非常推薦的一種state backend。也是未來版本的默認模式,我們這裏沒有使用。

5.7、實現了哪些業務

這裏不打算詳細介紹業務,只說下我們基於Flink data streaming,實現了對股票、基金、期貨、指數以及板塊的實時統計,並在基礎上對某些特殊的指標進行了一些策略的處理。

6、總結

Apache Flink在流處理方面的諸多特性,以及我們自身的業務需求,是促使我學習並實踐Flink的根本原因。

目前國內使用Flink的公司主要是阿里(實際是二次開發後的Blink),並也投入到了實際的生產中。雖然數據量以及集羣規模無法相比,但是這也從側面反映出Flink是具有很大的潛力的。

最後,對於已經或者準備使用Flink的朋友,我把我曾經學習的資源(主要是網絡博客)共享:

1、Flink源碼 
2、官方文檔1.2 
3、官方博客 
4、Flink母公司data-Artisans官方博客 
5、Flink Training 
6、Madhukar’s Blog 
7、Flink1.1中文文檔(部分) 
8、The world beyond batch: Streaming 101 
9、The world beyond batch: Streaming 102 
10、VinoYang的專欄 
11、Jark’s Blog 
12、fxjwind

當然,還有些其他的朋友也在寫Flink相關的內容,大家可以自行google學習。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章