一文了解 Apache Flink 核心技术
作者:伍翀
Apache Flink 介紹
Apache Flink (以下簡稱Flink)是近年來越來越流行的一款開源大數(shù)據(jù)計(jì)算引擎,它同時支持了批處理和流處理,也能用來做一些基于事件的應(yīng)用。使用官網(wǎng)的語句來介紹 Flink 就是 "Stateful Computations Over Streams"。
首先 Flink 是一個純流式的計(jì)算引擎,它的基本數(shù)據(jù)模型是數(shù)據(jù)流。流可以是無邊界的無限流,即一般意義上的流處理。也可以是有邊界的有限流,這樣就是批處理。因此 Flink 用一套架構(gòu)同時支持了流處理和批處理。其次,Flink 的一個優(yōu)勢是支持有狀態(tài)的計(jì)算。如果處理一個事件(或一條數(shù)據(jù))的結(jié)果只跟事件本身的內(nèi)容有關(guān),稱為無狀態(tài)處理;反之結(jié)果還和之前處理過的事件有關(guān),稱為有狀態(tài)處理。稍微復(fù)雜一點(diǎn)的數(shù)據(jù)處理,比如說基本的聚合,數(shù)據(jù)流之間的關(guān)聯(lián)都是有狀態(tài)處理。
Apache Flink 基石
Apache Flink 之所以能越來越受歡迎,我們認(rèn)為離不開它最重要的四個基石:Checkpoint、State、Time、Window。
首先是Checkpoint機(jī)制,這是 Flink 最重要的一個特性。Flink 基于 Chandy-Lamport 算法實(shí)現(xiàn)了分布式一致性的快照,從而提供了 exactly-once 的語義。在 Flink 之前的流計(jì)算系統(tǒng)(如 Strom,Samza)都沒有很好地解決 exactly-once 的問題。提供了一致性的語義之后,Flink 為了讓用戶在編程時能夠更輕松、更容易地去管理狀態(tài),引入了托管狀態(tài)(managed state)并提供了 API 接口,讓用戶使用起來感覺就像在用 Java 的集合類一樣。除此之外,Flink 還實(shí)現(xiàn)了 watermark 的機(jī)制,解決了基于事件時間處理時的數(shù)據(jù)亂序和數(shù)據(jù)遲到的問題。最后,流計(jì)算中的計(jì)算一般都會基于窗口來計(jì)算,所以 Flink 提供了一套開箱即用的窗口操作,包括滾動窗口、滑動窗口、會話窗口,還支持非常靈活的自定義窗口以滿足特殊業(yè)務(wù)的需求。
Flink API 歷史變遷
在 Flink 1.0.0 時期,加入了 State API,即 ValueState、ReducingState、ListState 等等。State API 可以認(rèn)為是 Flink 里程碑式的創(chuàng)新,它能夠讓用戶像使用 Java 集合一樣地使用 Flink State,卻能夠自動享受到狀態(tài)的一致性保證,不會因?yàn)楣收隙鴣G失狀態(tài)。包括后來 Apache Beam 的 State API 也從中借鑒了很多。
在 Flink 1.1.0 時期,支持了 Session Window 并且能夠正確的處理亂序的遲到數(shù)據(jù),使得最終結(jié)果是正確的
在 Flink 1.2.0 時期,提供了 ProcessFunction,這是一個 Lower-level 的API,用于實(shí)現(xiàn)更高級更復(fù)雜的功能。它除了能夠注冊各種類型的 State 外,還支持注冊定時器(支持 EventTime 和 ProcessingTime),常用于開發(fā)一些基于事件、基于時間的應(yīng)用程序。
在 Flink 1.3.0 時期,提供了 Side Output 功能。算子的輸出一般只有一種輸出類型,但是有些時候可能需要輸出另外的類型,比如除了輸出主流外,還希望把一些異常數(shù)據(jù)、遲到數(shù)據(jù)以側(cè)邊流的形式進(jìn)行輸出,并分別交給下游不同節(jié)點(diǎn)進(jìn)行處理。簡而言之,Side Output 支持了多路輸出的功能。
在 Flink 1.5.0 時期,加入了BroadcastState。BroadcastState是對 State API 的一個擴(kuò)展。它用來存儲上游被廣播過來的數(shù)據(jù),這個 operator 的每個并發(fā)上存的BroadcastState里面的數(shù)據(jù)都是一模一樣的,因?yàn)樗菑纳嫌螐V播而來的?;谶@種State可以比較好地去解決 CEP 中的動態(tài)規(guī)則的功能,以及 SQL 中不等值Join的場景。
在 Flink 1.6.0 時期,提供了State TTL功能、DataStream Interval Join功能。State TTL實(shí)現(xiàn)了在申請某個State時候可以在指定一個生命周期參數(shù)(TTL),指定該state 過了多久之后需要被系統(tǒng)自動清除。在這個版本之前,如果用戶想要實(shí)現(xiàn)這種狀態(tài)清理操作需要使用ProcessFunction注冊一個Timer,然后利用Timer的回調(diào)手動把這個State 清除。從該版本開始,Flink框架可以基于TTL原生地解決這件事情。DataStream Interval Join 使得 區(qū)間Join成為可能。例如左流的每一條數(shù)據(jù)去Join右流前后5分鐘之內(nèi)的數(shù)據(jù),這種就是5分鐘的區(qū)間Join。
Flink High-Level API 歷史變遷
在 Flink 1.0.0 時期,Table API (結(jié)構(gòu)化數(shù)據(jù)處理API)和 CEP(復(fù)雜事件處理API)這兩個框架被首次加入到倉庫中。Table API 是一種結(jié)構(gòu)化的高級 API,支持 Java 語言和 Scala 語言,類似于 Spark 的 DataFrame API。Table API 和 SQL非常相近,他們都是一種處理結(jié)構(gòu)化數(shù)據(jù)的語言,實(shí)現(xiàn)上可以共用很多內(nèi)容。所以在 Flink 1.1.0 里面,社區(qū)基于Apache Calcite對整個 Table 模塊做了重構(gòu),使得同時支持了 Table API 和 SQL 并共用了大部分代碼。
在 Flink 1.2.0 時期,社區(qū)在Table API和SQL上支持豐富的內(nèi)置窗口操作,包括Tumbling Window、Sliding Window、Session Window。
在 Flink 1.3.0 時期,社區(qū)首次提出了Dynamic Table這個概念,借助Dynamic Table,流和批之間可以相互進(jìn)行轉(zhuǎn)換。流可以是一張表,表也可以是一張流,這是流批統(tǒng)一的基礎(chǔ)之一。其中Retraction機(jī)制是實(shí)現(xiàn)Dynamic Table的基礎(chǔ)之一,基于Retraction才能夠正確地實(shí)現(xiàn)多級Aggregate、多級Join,才能夠保證流式 SQL 的語義與結(jié)果的正確性。另外,在該版本中還支持了 CEP 算子的可伸縮容(即改變并發(fā))。
在 Flink 1.5.0 時期,在 Table API 和 SQL 上支持了Join操作,包括無限流的 Join 和帶窗口的 Join。還添加了 SQL CLI 支持。SQL CLI 提供了一個類似Shell命令的對話框,可以交互式執(zhí)行查詢。
Flink Checkpoint & Recovery 歷史變遷
Checkpoint機(jī)制在Flink很早期的時候就已經(jīng)支持,是Flink一個很核心的功能,Flink 社區(qū)也一直努力提升 Checkpoint 和 Recovery 的效率。在 Flink 1.0.0 時期,提供了 RocksDB 狀態(tài)后端的支持,在這個版本之前所有的狀態(tài)數(shù)據(jù)只能存在進(jìn)程的內(nèi)存里面,JVM 內(nèi)存是固定大小的,隨著數(shù)據(jù)越來越多總會發(fā)生 FullGC 和 OOM 的問題,所以在生產(chǎn)環(huán)境中很難應(yīng)用起來。如果想要存更多數(shù)據(jù)、更大的State就要用到 RocksDB。RocksDB是一款基于文件的嵌入式數(shù)據(jù)庫,它會把數(shù)據(jù)存到磁盤,同時又提供高效的讀寫性能。所以使用RocksDB不會發(fā)生OOM這種事情。
在 Flink 1.1.0 時期,支持了 RocksDB Snapshot 的異步化。在之前的版本,RocksDB 的 Snapshot 過程是同步的,它會阻塞主數(shù)據(jù)流的處理,很影響吞吐量。在支持異步化之后,吞吐量得到了極大的提升。
在 Flink 1.2.0 時期,通過引入KeyGroup的機(jī)制,支持了 KeyedState 和 OperatorState 的可擴(kuò)縮容。也就是支持了對帶狀態(tài)的流計(jì)算任務(wù)改變并發(fā)的功能。
在 Flink 1.3.0 時期,支持了 Incremental Checkpoint (增量檢查點(diǎn))機(jī)制。Incemental Checkpoint 的支持標(biāo)志著 Flink 流計(jì)算任務(wù)正式達(dá)到了生產(chǎn)就緒狀態(tài)。增量檢查點(diǎn)是每次只將本次 checkpoint 期間新增的狀態(tài)快照并持久化存儲起來。一般流計(jì)算任務(wù),GB 級別的狀態(tài),甚至 TB 級別的狀態(tài)是非常常見的,如果每次都把全量的狀態(tài)都刷到分布式存儲中,這個效率和網(wǎng)絡(luò)代價是很大的。如果每次只刷新增的數(shù)據(jù),效率就會高很多。在這個版本里面還引入了細(xì)粒度的recovery的功能,細(xì)粒度的recovery在做恢復(fù)的時候,只需要恢復(fù)失敗節(jié)點(diǎn)的聯(lián)通子圖,不用對整個 Job 進(jìn)行恢復(fù),這樣便能夠提高恢復(fù)效率。
在 Flink 1.5.0 時期,引入了本地狀態(tài)恢復(fù)的機(jī)制。因?yàn)榛赾heckpoint機(jī)制,會把State持久化地存儲到某個分布式存儲,比如HDFS,當(dāng)發(fā)生 failover 的時候需要重新把數(shù)據(jù)從遠(yuǎn)程HDFS再下載下來,如果這個狀態(tài)特別大那么下載耗時就會較長,failover 恢復(fù)所花的時間也會拉長。本地狀態(tài)恢復(fù)機(jī)制會提前將狀態(tài)文件在本地也備份一份,當(dāng)Job發(fā)生failover之后,恢復(fù)時可以在本地直接恢復(fù),不需從遠(yuǎn)程HDFS重新下載狀態(tài)文件,從而提升了恢復(fù)的效率。
Flink Runtime 歷史變遷
在 Flink 1.2.0 時期,提供了Async I/O功能。Async I/O 是阿里巴巴貢獻(xiàn)給社區(qū)的一個呼聲非常高的特性,主要目的是為了解決與外部系統(tǒng)交互時網(wǎng)絡(luò)延遲成為了系統(tǒng)瓶頸的問題。例如,為了關(guān)聯(lián)某些字段需要查詢外部 HBase 表,同步的方式是每次查詢的操作都是阻塞的,數(shù)據(jù)流會被頻繁的I/O請求卡住。當(dāng)使用異步I/O之后就可以同時地發(fā)起N個異步查詢的請求,不會阻塞主數(shù)據(jù)流,這樣便提升了整個job的吞吐量,提升CPU利用率。
在 Flink 1.3.0 時期,引入了HistoryServer的模塊。HistoryServer主要功能是當(dāng)job結(jié)束以后,會把job的狀態(tài)以及信息都進(jìn)行歸檔,方便后續(xù)開發(fā)人員做一些深入排查。
在 Flink 1.4.0 時期,提供了端到端的 exactly-once 的語義保證。Exactly-once 是指每條輸入的數(shù)據(jù)只會作用在最終結(jié)果上有且只有一次,即使發(fā)生軟件或硬件的故障,不會有丟數(shù)據(jù)或者重復(fù)計(jì)算發(fā)生。而在該版本之前,exactly-once 保證的范圍只是 Flink 應(yīng)用本身,并不包括輸出給外部系統(tǒng)的部分。在 failover 時,這就有可能寫了重復(fù)的數(shù)據(jù)到外部系統(tǒng),所以一般會使用冪等的外部系統(tǒng)來解決這個問題。在 Flink 1.4 的版本中,Flink 基于兩階段提交協(xié)議,實(shí)現(xiàn)了端到端的 exactly-once 語義保證。內(nèi)置支持了 Kafka 的端到端保證,并提供了 TwoPhaseCommitSinkFunction 供用于實(shí)現(xiàn)自定義外部存儲的端到端 exactly-once 保證。
在 Flink 1.5.0 時期,Flink 發(fā)布了新的部署模型和處理模型(FLIP6)。新部署模型的開發(fā)工作已經(jīng)持續(xù)了很久,該模型的實(shí)現(xiàn)對Flink核心代碼改動特別大,可以說是自 Flink 項(xiàng)目創(chuàng)建以來,Runtime 改動最大的一次。簡而言之,新的模型可以在YARN, MESOS調(diào)度系統(tǒng)上更好地動態(tài)分配資源、動態(tài)釋放資源,并實(shí)現(xiàn)更高的資源利用率,還有提供更好的作業(yè)之間的隔離。
除了 FLIP6 的改進(jìn),在該版本中,還對網(wǎng)站棧做了重構(gòu)。重構(gòu)的原因是在老版本中,上下游多個 task 之間的通信會共享同一個 TCP connection,導(dǎo)致某一個 task 發(fā)生反壓時,所有共享該連接的 task 都會被阻塞,反壓的粒度是 TCP connection 級別的。為了改進(jìn)反壓機(jī)制,Flink應(yīng)用了在解決網(wǎng)絡(luò)擁塞時一種經(jīng)典的流控方法——基于Credit的流量控制。使得流控的粒度精細(xì)到具體某個 task 級別,有效緩解了反壓對吞吐量的影響。
總結(jié)
Flink 同時支持了流處理和批處理,目前流計(jì)算的模型已經(jīng)相對比較成熟和領(lǐng)先,也經(jīng)歷了各個公司大規(guī)模生產(chǎn)的驗(yàn)證。社區(qū)在接下來將繼續(xù)加強(qiáng)流計(jì)算方面的性能和功能,包括對 Flink SQL 擴(kuò)展更豐富的功能和引入更多的優(yōu)化。另一方面也將加大力量提升批處理、機(jī)器學(xué)習(xí)等生態(tài)上的能力。
更多資訊請?jiān)L問 Apache Flink 中文社區(qū)網(wǎng)站
總結(jié)
以上是生活随笔為你收集整理的一文了解 Apache Flink 核心技术的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU 6070 Dirt Ratio(
- 下一篇: spring statemachine的