(基于最新的Kafka version 0.10.2 new consumer API )想要Spark Streaming精確一次消費Topic?拿去不謝,記得點贊和分享!

本文基于Spark2.1.0、Kafka 0.10.2、Scala 2.11.8版本

背景:

Kafka做為一款流行的分布式發布訂閱消息系統,以高吞吐、低延時、高可靠的特點著稱,已經成為Spark Streaming常用的流數據來源。

常用的ETL架構


1,Kafka Topic的消費保證:

流數據,顧名思義,就是無界的、流動的、快速的、連續到達的數據序列,所以它不像可靠的文件系統(如HDFS)在計算出現故障時,可以隨時恢復數據來重新計算。

那么,如何保證流數據可靠的傳遞呢?我們先了解下面的概念:

Producer通過Broker傳遞消息給Consumer,Consumer消費消息,

P-B-C 3者之間的傳輸,主要有以下幾種可能的場景:

At most once(最多傳輸一次): 消息可能會丟,但絕不會重復傳輸;

At least one ?(至少傳輸一次): ?消息絕不會丟,但可能會重復傳輸;

Exactly once(精確傳輸一次): ?每條消息肯定會被傳輸一次且僅傳輸一次,不會重復.

3種場景,適合不同生產環境的需要,相關介紹網上很多,這里就不多說了。

本文的重點是,如何用Spark 2.1.0、Kafka 0.10.2、spark-streaming-kafka-0-10_2.11-2.1.0.jar、HBase 1.3.0來配合實現消息Exactly once(精確一次)的傳遞和消費。網上相關的scala或者java代碼,都是基于老版本的API,目前沒有發現基于new Kafka consumer API的實現,所以看到本文覺得有收獲的同學,就給點個贊吧。

2,Kafka 0.10.2版本介紹:

Kafka 0.10.2版本,為了和Zookeeper的解耦,較之前的版本有了很大的變化,老版本的高級API和簡單API的說法不見了,取而代之的是New Consumer API及New Consumer Configs,相關接口的參數及P-B-C 3者的配置參數有了很多改動。

Spark官方與之配合的工具包spark-streaming-kafka-0-10_2.11-2.1.0.jar 也做了相應的改變,取消了KafkaCluster類、取消了ZkUtils.updatePersistentPath等多個方法,也都是為了不在將Topic offset由zookeeper自動保存,而由用戶靈活的選擇Kafka和Spark 2.1.0官方提供的幾種方法來保存offset,最好的使用情況下,端到端的業務可以達到精確一次的消費保證。

(為了美觀,本文相關的java代碼都用貼圖方式展現了,最終實現的端到端精確一次消費消息的源碼見文末的鏈接)

3,Kafka官方提供的多種消費保證:

Consumer的3個重要的配置,需要配合使用,來達到Broker到Consumer之間精確一次的消費保證。

請看這些參數的組合(有點繞,請仔細看)

(enable.auto.commit:false) + (auto.offset.reset:earliest):

在Broker到Consumer之間實現了至少一次語義,因為不使用Kafka提供的自動保存offset功能,每次應用程序啟動時,都是從Topic的初始位置來獲取消息。也就是說,應用程序因為故障失敗,或者是人為的停止,再次啟動應用程序時,都會從初始位置把指定的Topic所有的消息都消費一遍,這就導致了Consumer會重復消費。

(enable.auto.commit:false) + (auto.offset.reset:latest):

在Broker到Consumer之間實現了至多一次語義,因為不使用Kafka提供的自動保存offset功能,每次應用程序啟動時,都是從Topic的末尾位置來獲取消息。也就是說,應用程序因為故障失敗,或者是人為的停止后,如果Producer向Broker發送新的消息,當再次啟動應用程序時,Consumer從指定的Topic的末尾來開始消費,這就導致了這部分新產生的消息丟失。

(enable.auto.commit:true)+(auto.offset.reset:earliest)+(auto.commit.interval.ms) :

在Broker到Consumer之間實現了精確一次語義,因為使用了Kafka提供的自動保存offset功能,當應用程序第一次啟動時,首先從Topic的初試位置來獲取消息,原有的消息一個都沒有丟失;緊接著,在auto.commit.interval.ms時間后,Kafka會使用coordinator協議commit當前的offset(topic的每個分區的offset)。當應用程序因為故障失敗,或者是人為的停止,再次啟動應用程序時,都會從coordinator模塊獲取Topic的offset,從上一次消費結束的位置繼續消費,所以不會重復消費已經消費過的消息,也不會丟失在應用程序停止期間新產生的消息,做到了Broker到Consumer之間精確一次的傳遞。

下面是Kafka 0.10.2 ConsumerCoordinator.java的源碼片段,用戶配置enable.auto.commit:true對應的代碼是autoCommitEnabled為true,最終調用doAutoCommitOffsetsAsync,使用coordinator協議保存offset(注意,最新版本已經和zookeeper解耦,不會把offset保存在zookeeper中,所以通過zkCli.sh是看不到相關topic的)

下面是實現的Spark Streaming代碼。

當然,這還遠遠不夠,因為這樣的方式,會出現業務兩段性的后果:

1,讀完消息先commit再處理消息。這種模式下,如果consumer在commit后還沒來得及處理消息就crash了,下次重新開始工作后就無法讀到剛剛已提交而未處理的消息,這就對應于At most once;

2,讀完消息先處理再commit。這種模式下,如果處理完了消息在commit之前consumer crash了,下次重新開始工作時還會處理剛剛未commit的消息,實際上該消息已經被處理過了。這就對應于At least once。

所以,要想實現端到端消息的精確一次消費,還需要耐心往后看。

3,Spark官方提供的多種消費保證:(基于spark-streaming-kafka-0-10_2.11-2.1.0.jar,相比前一個版本有很多改變)

CheckPoint:

通過設置Driver程序的checkpoint,來保存topic offset。這種方法很簡單,但是缺陷也很大:應用程序有改變時,無法使用原來的checkpoint來恢復offset;只能滿足Broker到Consumer之間精確一次的傳遞。

當應用程序第一次啟動時,首先從Topic的初試位置來獲取消息,原有的消息一個都沒有丟失;緊接著,在batch時間到達后,Spark會使用checkpoint保存當前的offset(topic的每個分區的offset)。當應用程序失敗或者人為停止后,再次啟動應用程序時,都會從checkpoint恢復Topic的offset,從上一次消費結束的位置繼續消費,所以不會重復消費已經消費過的消息,也不會丟失在應用程序停止期間新產生的消息。

實現的Spark Streaming代碼如下(注意:Spark 1.6.3之后,檢查checkpoint的實現已經不在用JavaStreamingContextFactory工廠操作了,請細看我的代碼是怎么做的)

Kafka itself:

和前面提到的enable.auto.commit:true異曲同工,不過這里用commitAsync方法異步的把offset提交給Kafka 。當應用程序第一次啟動時,首先從Topic的初試位置來獲取消息,原有的消息一個都沒有丟失;緊接著,用commitAsync方法異步的把offset提交給Kafka(topic的每個分區的offset)。當應用程序失敗或者人為停止后,再次啟動應用程序時,都會從kafka恢復Topic的offset,從上一次消費結束的位置繼續消費,所以不會重復消費已經消費過的消息,也不會丟失在應用程序停止期間新產生的消息。

與checkpoint相比,應用程序代碼的更改不會影響offset的存儲和獲取。然而,這樣的操作不是事務性的,由于是異步提交offset,當提交offset過程中應用程序crash,則無法保存正確的offset,會導致消息丟失或者重復消費。

實現的Spark Streaming代碼如下:

Your own data store:(當當當當,好戲出場)

如果要做到消息端到端的Exactly once消費,就需要事務性的處理offset和實際操作的輸出。

經典的做法讓offset和操作輸出存在同一個地方,會更簡潔和通用。比如,consumer把最新的offset和加工后的數據一起寫到HBase中,那就可以保證數據的輸出和offset的更新要么都成功,要么都失敗,間接實現事務性,最終做到消息的端到端的精確一次消費。(新版本的官網中只字未提使用Zookeeper保存offset,是有多嫌棄??)

實現的Spark Streaming代碼如下(ConsumerRecord類不能序列化,使用時要注意,不要分發該類到其他工作節點上,避免錯誤打印)

其實說白了,官方提供的思路就是,把JavaInputDStream轉換為OffsetRange對象,該對象具有topic對應的分區的所有信息,每次batch處理完,Spark Streaming都會自動更新該對象,所以你只需要找個合適的地方保存該對象(比如HBase、HDFS),就可以愉快的操縱offset了。

4,相關鏈接

本文實現的精確一次消費的Java源代碼

Spark Streaming + Kafka Integration Guide (Kafka broker version 0.10.0 or higher)

Kafka 0.10.2 Documentation

(如需轉載,請標明作者和出處)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容