Flink Connector 深度解析

作者介紹:董亭亭,快手大數據架構實時計算引擎團隊負責人。目前負責 Flink 引擎在快手內的研發、應用以及周邊子系統建設。2013 年畢業於大連理工大學,曾就職於奇虎 360、58 集團。主要研究領域包括:分佈式計算、調度系統、分佈式存儲等系統。

<!--more-->

本文主要分享Flink connector相關內容,分爲以下三個部分的內容:第一部分會首先介紹一下Flink Connector有哪些。第二部分會重點介紹在生產環境中經常使用的kafka connector的基本的原理以及使用方法。第三部分答疑環節,看大家有沒有一些問題。

Flink Streaming Connector

Flink是新一代流批統一的計算引擎,它需要從不同的第三方存儲引擎中把數據讀過來,進行處理,然後再寫出到另外的存儲引擎中。Connector的作用就相當於一個連接器,連接 Flink 計算引擎跟外界存儲系統。Flink裏有以下幾種方式,當然也不限於這幾種方式可以跟外界進行數據交換:第一種 Flink裏面預定義了一些source和sink。第二種 FLink內部也提供了一些Boundled connectors。第三種 可以使用第三方apache Bahir項目中提供的連接器。第四種是通過異步IO方式。下面分別簡單介紹一下這四種數據讀寫的方式。

預定義的source和sink

Flink裏預定義了一部分source和sink。在這裏分了幾類。

  • 基於文件的source和sink。

如果要從文本文件中讀取數據,可以直接使用

  • env.readTextFile(path)

就可以以文本的形式讀取該文件中的內容。當然也可以使用

  • env.readFile(fileInputFormat, path)

根據指定的fileInputFormat格式讀取文件中的內容。

如果數據在FLink內進行了一系列的計算,想把結果寫出到文件裏,也可以直接使用內部預定義的一些sink,比如將結果已文本或csv格式寫出到文件中,可以使用DataStream的writeAsText(path)和 writeAsCsv(path)。

  • 基於Socket的Source和Sink

提供Socket的host name及port,可以直接用StreamExecutionEnvironment預定的接口socketTextStream創建基於Socket的source,從該socket中以文本的形式讀取數據。當然如果想把結果寫出到另外一個Socket,也可以直接調用DataStream writeToSocket。

  • 基於內存 Collections、Iterators 的Source可以直接基於內存中的集合或者迭代器,調用StreamExecutionEnvironment fromCollection、fromElements構建相應的source。結果數據也可以直接print、printToError的方式寫出到標準輸出或標準錯誤。

詳細也可以參考Flink源碼中提供的一些相對應的Examples來查看異常預定義source和sink的使用方法,例如WordCount、SocketWindowWordCount。

Bundled Connectors

Flink裏已經提供了一些綁定的Connector,例如kafka source和sink,Es sink等。讀寫kafka、es、rabbitMQ時可以直接使用相應connector的api即可。第二部分會詳細介紹生產環境中最常用的kafka connector。

雖然該部分是Flink 項目源代碼裏的一部分,但是真正意義上不算作flink引擎相關邏輯,並且該部分沒有打包在二進制的發佈包裏面。所以在提交Job時候需要注意,job代碼jar包中一定要將相應的connetor相關類打包進去,否則在提交作業時就會失敗,提示找不到相應的類,或初始化某些類異常。

Apache Bahir中的連接器

Apache Bahir 最初是從 Apache Spark 中獨立出來項目提供,以提供不限於 Spark 相關的擴展/插件、連接器和其他可插入組件的實現。通過提供多樣化的流連接器(streaming connectors)和 SQL 數據源擴展分析平臺的覆蓋面。如有需要寫到flume、redis的需求的話,可以使用該項目提供的connector。

Async I/O

流計算中經常需要與外部存儲系統交互,比如需要關聯mysql中的某個表。一般來說,如果用同步I/O的方式,會造成系統中出現大的等待時間,影響吞吐和延遲。爲了解決這個問題,異步I/O可以併發處理多個請求,提高吞吐,減少延遲。

Async的原理可參考官方文檔:<https://ci.apache.org/project...

Flink Kafka Connector

本章重點介紹生產環境中最常用到的Flink kafka connector。使用flink的同學,一定會很熟悉kafka,它是一個分佈式的、分區的、多副本的、 支持高吞吐的、發佈訂閱消息系統。生產環境環境中也經常會跟kafka進行一些數據的交換,比如利用kafka consumer讀取數據,然後進行一系列的處理之後,再將結果寫出到kafka中。這裏會主要分兩個部分進行介紹,一是Flink kafka Consumer,一個是Flink kafka Producer。

首先看一個例子來串聯下Flink kafka connector。代碼邏輯裏主要是從kafka裏讀數據,然後做簡單的處理,再寫回到kafka中。

分別用紅色框 框出 如何構造一個Source sink Function. Flink提供了現成的構造FLinkKafkaConsumer、Producer的接口,可以直接使用。這裏需要注意,因爲kafka有多個版本,多個版本之間的接口協議會不同。Flink針對不同版本的kafka有相應的版本的Consumer和Producer。例如:針對08、09、10、11版本,Flink對應的consumer分別是FlinkKafkaConsumer08、09、010、011,producer也是。

Flink kafka Consumer

反序列化數據

因爲kafka中數據都是以二進制byte形式存儲的。讀到flink系統中之後,需要將二進制數據轉化爲具體的java、scala對象。具體需要實現一個schema類,定義如何序列化和反序列數據。反序列化時需要實現DeserializationSchema接口,並重寫deserialize(byte[] message)函數,如果是反序列化kafka中kv的數據時,需要實現KeyedDeserializationSchema接口,並重寫deserialize(byte[] messageKey, byte[] message, String topic, int partition, long offset)函數。

另外Flink中也提供了一些常用的序列化反序列化的schema類。例如,SimpleStringSchema,按字符串方式進行序列化、反序列化。TypeInformationSerializationSchema,它可根據Flink的TypeInformation信息來推斷出需要選擇的schema。JsonDeserializationSchema 使用jackson反序列化json格式消息,並返回ObjectNode,可以使用.get(“property”)方法來訪問相應字段。

消費起始位置設置

如何設置作業從kafka消費數據最開始的起始位置,這一部分flink也提供了非常好的封裝。在構造好的FlinkKafkaConsumer類後面調用如下相應函數,設置合適的其實位置。

setStartFromGroupOffsets,也是默認的策略,從group offset位置讀取數據,group offset指的是kafka broker端記錄的某個group的最後一次的消費位置。但是kafka broker端沒有該group信息,會根據kafka的參數"auto.offset.reset"的設置來決定從哪個位置開始消費。

setStartFromEarliest,從kafka最早的位置開始讀取。

setStartFromLatest,從kafka最新的位置開始讀取。

setStartFromTimestamp(long),從時間戳大於或等於指定時間戳的位置開始讀取。Kafka時戳,是指kafka爲每條消息增加另一個時戳。該時戳可以表示消息在proudcer端生成時的時間、或進入到kafka broker時的時間。

setStartFromSpecificOffsets,從指定分區的offset位置開始讀取,如指定的offsets中不存某個分區,該分區從group offset位置開始讀取。此時需要用戶給定一個具體的分區、offset的集合。

一些具體的使用方法可以參考下圖。需要注意的是,因爲flink框架有容錯機制,如果作業故障,如果作業開啓checkpoint,會從上一次checkpoint狀態開始恢復。或者在停止作業的時候主動做savepoint,啓動作業時從savepoint開始恢復。這兩種情況下恢復作業時,作業消費起始位置是從之前保存的狀態中恢復,與上面提到跟kafka這些單獨的配置無關。

topic和partition動態發現

實際的生產環境中可能有這樣一些需求,比如場景一,有一個flink作業需要將五份數據聚合到一起,五份數據對應五個kafka topic,隨着業務增長,新增一類數據,同時新增了一個kafka topic,如何在不重啓作業的情況下作業自動感知新的topic。場景二,作業從一個固定的kafka topic讀數據,開始該topic有10個partition,但隨着業務的增長數據量變大,需要對kafka partition個數進行擴容,由10個擴容到20。該情況下如何在不重啓作業情況下動態感知新擴容的partition?

針對上面的兩種場景,首先需要在構建FlinkKafkaConsumer時的properties中設置flink.partition-discovery.interval-millis參數爲非負值,表示開啓動態發現的開關,以及設置的時間間隔。此時FLinkKafkaConsumer內部會啓動一個單獨的線程定期去kafka獲取最新的meta信息。針對場景一,還需在構建FlinkKafkaConsumer時,topic的描述可以傳一個正則表達式描述的pattern。每次獲取最新kafka meta時獲取正則匹配的最新topic列表。針對場景二,設置前面的動態發現參數,在定期獲取kafka最新meta信息時會匹配新的partition。爲了保證數據的正確性,新發現的partition從最早的位置開始讀取。

commit offset方式

Flink kafka consumer commit offset方式需要區分是否開啓了checkpoint。

如果checkpoint關閉,commit offset要依賴於kafka客戶端的auto commit。需設置enable.auto.commit, auto.commit.interval.ms 參數到consumer properties,就會按固定的時間間隔定期auto commit offset到kafka。

如果開啓checkpoint,這個時候作業消費的offset是Flink在state中自己管理和容錯。此時提交offset到kafka,一般都是作爲外部進度的監控,想實時知道作業消費的位置和lag情況。此時需要setCommitOffsetsOnCheckpoints爲true來設置當checkpoint成功時提交offset到kafka。此時commit offset的間隔就取決於checkpoint的間隔,所以此時從kafka一側看到的lag可能並非完全實時,如果checkpoint間隔比較長lag曲線可能會是一個鋸齒狀。

Timestamp Extraction/Watermark生成

我們知道當flink作業內使用EventTime屬性時,需要指定從消息中提取時戳和生成水位的函數。FlinkKakfaConsumer構造的source後直接調用assignTimestampsAndWatermarks函數設置水位生成器的好處是此時是每個partition一個watermark assigner,如下圖。source生成的睡戳爲多個partition時戳對齊後的最小時戳。此時在一個source讀取多個partition,並且partition之間數據時戳有一定差距的情況下,因爲在source端watermark在partition級別有對齊,不會導致數據讀取較慢partition數據丟失。

Flink kafka Producer

Producer 分區

使用FlinkKafkaProducer往kafka中寫數據時,如果不單獨設置partition策略,會默認使用FlinkFixedPartitioner,該partitioner分區的方式是task所在的併發id對topic 總partition數取餘:parallelInstanceId % partitions.length。此時如果sink爲4,paritition爲1,則4個task往同一個partition中寫數據。但當sink task< partition 個數時會有部分partition沒有數據寫入,例如sink task爲2,partition總數爲4,則後面兩個partition將沒有數據寫入。如果構建FlinkKafkaProducer時,partition設置爲null,此時會使用kafka producer默認分區方式,非key寫入的情況下,使用round-robin的方式進行分區,每個task都會輪訓的寫下游的所有partition。該方式下游的partition數據會比較均衡,但是缺點是partition個數過多的情況下維持過多的網絡鏈接,即每個task都會維持跟所有partition所在broker的鏈接。

容錯

Flink kafka 09、010版本下,通過setLogFailuresOnly爲false,setFlushOnCheckpoint爲true,能達到at-least-once語義。setLogFailuresOnly,默認爲false,是控制寫kafka失敗時,是否只打印失敗的log不拋異常讓作業停止。setFlushOnCheckpoint,默認爲true,是控制是否在checkpoint時fluse數據到kafka,保證數據已經寫到kafka。否則數據有可能還緩存在kafka 客戶端的buffer中,並沒有真正寫出到kafka,此時作業掛掉數據即丟失,不能做到至少一次的語義。

Flink kafka 011版本下,通過兩階段提交的sink結合kafka事務的功能,可以保證端到端精準一次。詳細原理可以參考:https://www.ververica.com/blo...

Q&A

(1)在flink consumer的並行度的設置:是對應topic的partitions個數嗎?要是有多個主題數據源,並行度是設置成總體的partitions數嗎?
答:這個並不是絕對的,跟topic的數據量也有關,如果數據量不大,也可以設置小於partitions個數的併發數。但不要設置併發數大於partitions總數,因爲這種情況下某些併發因爲分配不到partition導致沒有數據處理。

(2)如果 partitioner 傳 null 的時候是 round-robin 發到每一個partition?如果有 key 的時候行爲是 kafka 那種按照 key 分佈到具體分區的行爲嗎?
答:如果在構造FlinkKafkaProducer時,如果沒有設置單獨的partitioner,則默認使用FlinkFixedPartitioner,此時無論是帶key的數據,還是不帶key。如果主動設置partitioner爲null時,不帶key的數據會round-robin的方式寫出,帶key的數據會根據key,相同key數據分區的相同的partition,如果key爲null,再輪詢寫。不帶key的數據會輪詢寫各partition。

(3)如果checkpoint時間過長,offset未提交到kafka,此時節點宕機了,重啓之後的重複消費如何保證呢?
首先開啓checkpoint時offset是flink通過狀態state管理和恢復的,並不是從kafka的offset位置恢復。在checkpoint機制下,作業從最近一次checkpoint恢復,本身是會回放部分歷史數據,導致部分數據重複消費,Flink引擎僅保證計算狀態的精準一次,要想做到端到端精準一次需要依賴一些冪等的存儲系統或者事務操作。

關注我

微信公衆號:zhisheng

另外我自己整理了些 Flink 的學習資料,目前已經全部放到微信公衆號(zhisheng)了,你可以回覆關鍵字:Flink 即可無條件獲取到。另外也可以加我微信 你可以加我的微信:yuanblog_tzs,探討技術!

更多私密資料請加入知識星球!

Github 代碼倉庫

https://github.com/zhisheng17/flink-learning/

以後這個項目的所有代碼都將放在這個倉庫裏,包含了自己學習 flink 的一些 demo 和博客

博客

1、Flink 從0到1學習 —— Apache Flink 介紹

2、Flink 從0到1學習 —— Mac 上搭建 Flink 1.6.0 環境並構建運行簡單程序入門

3、Flink 從0到1學習 —— Flink 配置文件詳解

4、Flink 從0到1學習 —— Data Source 介紹

5、Flink 從0到1學習 —— 如何自定義 Data Source ?

6、Flink 從0到1學習 —— Data Sink 介紹

7、Flink 從0到1學習 —— 如何自定義 Data Sink ?

8、Flink 從0到1學習 —— Flink Data transformation(轉換)

9、Flink 從0到1學習 —— 介紹 Flink 中的 Stream Windows

10、Flink 從0到1學習 —— Flink 中的幾種 Time 詳解

11、Flink 從0到1學習 —— Flink 讀取 Kafka 數據寫入到 ElasticSearch

12、Flink 從0到1學習 —— Flink 項目如何運行?

13、Flink 從0到1學習 —— Flink 讀取 Kafka 數據寫入到 Kafka

14、Flink 從0到1學習 —— Flink JobManager 高可用性配置

15、Flink 從0到1學習 —— Flink parallelism 和 Slot 介紹

16、Flink 從0到1學習 —— Flink 讀取 Kafka 數據批量寫入到 MySQL

17、Flink 從0到1學習 —— Flink 讀取 Kafka 數據寫入到 RabbitMQ

18、Flink 從0到1學習 —— Flink 讀取 Kafka 數據寫入到 HBase

19、Flink 從0到1學習 —— Flink 讀取 Kafka 數據寫入到 HDFS

20、Flink 從0到1學習 —— Flink 讀取 Kafka 數據寫入到 Redis

21、Flink 從0到1學習 —— Flink 讀取 Kafka 數據寫入到 Cassandra

22、Flink 從0到1學習 —— Flink 讀取 Kafka 數據寫入到 Flume

23、Flink 從0到1學習 —— Flink 讀取 Kafka 數據寫入到 InfluxDB

24、Flink 從0到1學習 —— Flink 讀取 Kafka 數據寫入到 RocketMQ

25、Flink 從0到1學習 —— 你上傳的 jar 包藏到哪裏去了

26、Flink 從0到1學習 —— 你的 Flink job 日誌跑到哪裏去了

27、阿里巴巴開源的 Blink 實時計算框架真香

28、Flink 從0到1學習 —— Flink 中如何管理配置?

29、Flink 從0到1學習—— Flink 不可以連續 Split(分流)?

30、Flink 從0到1學習—— 分享四本 Flink 國外的書和二十多篇 Paper 論文

31、Flink 架構、原理與部署測試

32、爲什麼說流處理即未來?

33、OPPO 數據中臺之基石:基於 Flink SQL 構建實時數據倉庫

34、流計算框架 Flink 與 Storm 的性能對比

35、Flink狀態管理和容錯機制介紹

36、Apache Flink 結合 Kafka 構建端到端的 Exactly-Once 處理

37、360深度實踐:Flink與Storm協議級對比

38、如何基於Flink+TensorFlow打造實時智能異常檢測平臺?只看這一篇就夠了

39、Apache Flink 1.9 重大特性提前解讀

40、Flink 全網最全資源(視頻、博客、PPT、入門、實戰、源碼解析、問答等持續更新)

41、Flink 靈魂兩百問,這誰頂得住?

42、Flink 從0到1學習 —— 如何使用 Side Output 來分流?

43、你公司到底需不需要引入實時計算引擎?

44、一文讓你徹底瞭解大數據實時計算引擎 Flink

源碼解析

1、Flink 源碼解析 —— 源碼編譯運行

2、Flink 源碼解析 —— 項目結構一覽

3、Flink 源碼解析—— local 模式啓動流程

4、Flink 源碼解析 —— standalone session 模式啓動流程

5、Flink 源碼解析 —— Standalone Session Cluster 啓動流程深度分析之 Job Manager 啓動

6、Flink 源碼解析 —— Standalone Session Cluster 啓動流程深度分析之 Task Manager 啓動

7、Flink 源碼解析 —— 分析 Batch WordCount 程序的執行過程

8、Flink 源碼解析 —— 分析 Streaming WordCount 程序的執行過程

9、Flink 源碼解析 —— 如何獲取 JobGraph?

10、Flink 源碼解析 —— 如何獲取 StreamGraph?

11、Flink 源碼解析 —— Flink JobManager 有什麼作用?

12、Flink 源碼解析 —— Flink TaskManager 有什麼作用?

13、Flink 源碼解析 —— JobManager 處理 SubmitJob 的過程

14、Flink 源碼解析 —— TaskManager 處理 SubmitJob 的過程

15、Flink 源碼解析 —— 深度解析 Flink Checkpoint 機制

16、Flink 源碼解析 —— 深度解析 Flink 序列化機制

17、Flink 源碼解析 —— 深度解析 Flink 是如何管理好內存的?

18、Flink Metrics 源碼解析 —— Flink-metrics-core

19、Flink Metrics 源碼解析 —— Flink-metrics-datadog

20、Flink Metrics 源碼解析 —— Flink-metrics-dropwizard

21、Flink Metrics 源碼解析 —— Flink-metrics-graphite

22、Flink Metrics 源碼解析 —— Flink-metrics-influxdb

23、Flink Metrics 源碼解析 —— Flink-metrics-jmx

24、Flink Metrics 源碼解析 —— Flink-metrics-slf4j

25、Flink Metrics 源碼解析 —— Flink-metrics-statsd

26、Flink Metrics 源碼解析 —— Flink-metrics-prometheus

26、Flink Annotations 源碼解析

27、Flink 源碼解析 —— 如何獲取 ExecutionGraph ?

28、大數據重磅炸彈——實時計算框架 Flink

29、Flink Checkpoint-輕量級分佈式快照

30、Flink Clients 源碼解析
原文出處:zhisheng的博客,歡迎關注我的公衆號:zhisheng

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