Kafka源碼分析-系列1 -消息隊列的策略與語義

-Kafka關(guān)鍵概念介紹
-消息隊列的各種策略與語義

作為一個消息隊列不恭,Kafka在業(yè)界已經(jīng)相當(dāng)有名价淌。相對傳統(tǒng)的RabbitMq/ActiveMq杆故,Kafka天生就是分布式的霜威,支持?jǐn)?shù)據(jù)的分片、復(fù)制以及集群的方便擴(kuò)展欠拾。

與此同時梭域,Kafka是高可靠的石抡、持久化的消息隊列轻黑,并且這種可靠性沒有以犧牲性能為前提糊肤。

同時,在允許丟消息的業(yè)務(wù)場景下氓鄙,Kafka可以以非ACK馆揉、異步的方式來運行,從而最大程度的提高性能抖拦。

從本篇開始升酣,本序列將會由淺入深舷暮、從使用方式到原理再到源碼,全面的剖析Kafka這個消息中間件的方方面面噩茄。(所用Kafka源碼為0.9.0)

關(guān)鍵概念介紹

topic

以下是kafka的邏輯結(jié)構(gòu)圖: 每個topic也就是自定義的一個隊列下面,producer往隊列中放消息,consumer從隊列中取消息巢墅,topic之間相互獨立诸狭。

1.png

broker

與上圖對應(yīng)的是kafka的物理結(jié)構(gòu)圖:每個broker通常就是一臺物理機(jī)器券膀,在上面運行kafka server的一個實例君纫,所有這些broker實例組成kafka的服務(wù)器集群。

每個broker會給自己分配一個唯一的broker id芹彬。broker集群是通過zookeeper集群來管理的蓄髓。每個broker都會注冊到zookeeper上,有某個機(jī)器掛了舒帮,有新的機(jī)器加入会喝,zookeeper都會收到通知。

在0.9.0中玩郊,producer/consumer已經(jīng)不會依賴Zookeeper來獲取集群的配置信息肢执,而是通過任意一個broker來獲取整個集群的配置信息。如下圖所示:只有服務(wù)端依賴zk译红,客戶端不依賴zk预茄。

2.png

partition

kafka的topic,在每個機(jī)器上侦厚,是用文件存儲的耻陕。而這些文件呢,會分目錄刨沦。partition就是文件的目錄诗宣。比如一個topic叫abc,分了10個partion想诅,則在機(jī)器的目錄上召庞,就是:

abc_0
abc_1
abc_2
abc_3
...
abc_9

然后每個目錄里面,存放了一堆消息文件来破,消息是順序append log方式存儲的裁眯。關(guān)于這個,后面會詳細(xì)闡述讳癌。

replica/leader/follower

每個topic的partion的所有消息穿稳,都不是只存1份,而是在多個broker上冗余存儲晌坤,從而提高系統(tǒng)的可靠性逢艘。這多臺機(jī)器就叫一個replica集合旦袋。

在這個replica集合中,需要選出1個leader它改,剩下的是follower疤孕。也就是master/slave。

發(fā)送消息的時候央拖,只會發(fā)送給leader祭阀,然后leader再發(fā)給follower。

那這里面就有一個問題:leader收到消息之后鲜戒,是直接返回給producer呢专控,還是等所有follower都寫完消息之后,再返回遏餐? 關(guān)于這個伦腐,后面會相信闡述。

關(guān)鍵點:這里replica/leader/follower都是邏輯概念失都,并且是相對”partion”來講的柏蘑,而不是”topic”。也就說粹庞,同一個topic的不同partion咳焚,對于的replica集合可以是不一樣的。

比如

“abc-0” <1,3,5> //abc_0的replica集合是borker 1, 3, 5庞溜, leader是1革半, follower是3, 5
“abc-1” <1,3,7> //abc_1的replica集合是broker 1, 3, 7,leader是1, follower是3, 7
“abc_2” <3,7,9>
“abc_3” <1,7,9>
“abc_4” <1,3,5>

消息隊列的各種策略和語義

對于消息隊列的使用强缘,表面上看起來很簡單督惰,一端往里面放,一端從里面取旅掂。但就在這一放一取中赏胚,存在著諸多策略。

Producer的策略

是否ACK

所謂ACK商虐,是指服務(wù)器收到消息之后觉阅,是存下來之后,再給客戶端返回秘车,還是直接返回典勇。很顯然,是否ACK叮趴,是影響性能的一個重要指標(biāo)割笙。在kafka中,request.required.acks有3個取值,分別對應(yīng)3種策略:

request.required.acks

//0: 不等服務(wù)器ack就返回了伤溉,性能最高般码,可能丟數(shù)據(jù)
//1. leader確認(rèn)消息存下來了,再返回
//all: leader和所有的follower都確認(rèn)消息存下來了乱顾,再返回板祝。最可靠

備注:在0.9.0以前的版本,是用-1表示all

同步發(fā)送 vs 異步發(fā)送

所謂異步發(fā)送走净,就是指客戶端有個本地緩沖區(qū)券时,消息先存放到本地緩沖區(qū),然后有后臺線程來發(fā)送伏伯。

在0.8.2和0.8.2之前的版本中橘洞,同步發(fā)送和異步發(fā)送是分開實現(xiàn)的,用的Scala語言舵鳞。從0.8.2開始震檩,引入了1套新的Java版的client api琢蛤。在這套api中蜓堕,同步實際上是用異步間接實現(xiàn)的:

在異步發(fā)送下,有以下4個參數(shù)需要配置:

(1)隊列的最大長度
buffer.memory //缺省為33554432, 即32M

(2)隊列滿了博其,客戶端是阻塞套才,還是拋異常出來(缺省是true)
block.on.buffer.full
//true: 阻塞消息
//false:拋異常

(3)發(fā)送的時候,可以批量發(fā)送的數(shù)據(jù)量
batch.size //缺省16384字節(jié)慕淡,即16K

(4)最長等多長時間背伴,批量發(fā)送
linger.ms //缺省是0
//類似TCP/IP協(xié)議中的linger algorithm,> 0 表示發(fā)送的請求峰髓,會在隊列中積攥傻寂,然后批量發(fā)送。

很顯然携兵,異步發(fā)送可以提高發(fā)送的性能疾掰,但一旦客戶端掛了,就可能丟數(shù)據(jù)徐紧。

對于RabbitMQ, ActiveMQ静檬,他們都強(qiáng)調(diào)可靠性,因此不允許非ACK的發(fā)送并级,也沒有異步發(fā)送模式拂檩。Kafka提供了這個靈活性,允許使用者在性能與可靠性之間做權(quán)衡嘲碧。

(5)消息的最大長度
max.request.size //缺省是1048576稻励,即1M

這個參數(shù)會影響batch的大小,如果單個消息的大小 > batch的最大值(16k)愈涩,那么batch會相應(yīng)的增大

Consumer的策略

Push vs Pull

所有的消息隊列都要面對一個問題望抽,是broker把消息Push給消費者呢至非,還是消費者主動去broker Pull消息?

kafka選擇了pull的方式糠聪,為什么呢荒椭? 因為pull的方式更靈活:消息發(fā)送頻率應(yīng)該如何,消息是否可以延遲然后batch發(fā)送舰蟆,這些信息只有消費者自己最清楚趣惠!

因此把控制權(quán)交給消費者,消費者自己控制消費的速率身害,當(dāng)消費者處理消息很慢時味悄,它可以選擇減緩消費速率;當(dāng)處理消息很快時塌鸯,它可以選擇加快消費速率侍瑟。而在push的方式下,要實現(xiàn)這種靈活的控制策略丙猬,就需要額外的協(xié)議涨颜,讓消費者告訴broker,要減緩還是加快消費速率茧球,這增加了實現(xiàn)的復(fù)雜性庭瑰。

另外pull的方式下,消費者可以很容易的自適應(yīng)控制消息是batch的發(fā)送抢埋,還是最低限度的減少延遲弹灭,每來1個就發(fā)送1個。

消費的confirm

在消費端揪垄,所有消息隊列都要解決的一個問題就是“消費確認(rèn)問題”:消費者拿到一個消息穷吮,然后處理這個消息的時候掛了,如果這個時候broker認(rèn)為這個消息已經(jīng)消費了饥努,那這條消息就丟失了捡鱼。

一個解決辦法就是,消費者在消費完之后肪凛,再往broker發(fā)個confirm消息堰汉。broker收到confirm消息之后,再把消息刪除伟墙。

要實現(xiàn)這個翘鸭,broker就要維護(hù)每個消息的狀態(tài),已發(fā)送/已消費戳葵,很顯然就乓,這會增大broker的實現(xiàn)難度。同時,這還有另外一個問題生蚁,就是消費者消費完消息噩翠,發(fā)送confirm的時候,掛了邦投。這個時候會出現(xiàn)重復(fù)消費的問題伤锚。

kafka沒有直接解決這個問題,而是引入offset回退機(jī)制志衣,變相解決了這個問題屯援。在kafka里面,消息會存放一個星期念脯,才會被刪除狞洋。并且在一個partion里面,消息是按序號遞增的順序存放的绿店,因此消費者可以回退到某一個歷史的offset吉懊,進(jìn)行重新消費。

當(dāng)然假勿,對于重復(fù)消費的問題借嗽,需要消費者去解決。

broker的策略

消息的順序問題

在某些業(yè)務(wù)場景下废登,需要消息的順序不能亂:發(fā)送順序和消費順序要嚴(yán)格一致淹魄。而在kafka中郁惜,同一個topic堡距,被分成了多個partition,這多個partition之間是互相獨立的兆蕉。

之所以要分成多個partition羽戒,是為了提高并發(fā)度,多個partition并行的進(jìn)行發(fā)送/消費虎韵,但這卻沒有辦法保證消息的順序問題易稠。

一個解決辦法是,一個topic只用一個partition包蓝,但這樣很顯然限制了靈活性驶社。

還有一個辦法就是,所有發(fā)送的消息测萎,用同一個key亡电,這樣同樣的key會落在一個partition里面。

消息的刷盤機(jī)制

我們都知道硅瞧,操作系統(tǒng)本身是有page cache的份乒。即使我們用無緩沖的io,消息也不會立即落到磁盤上,而是在操作系統(tǒng)的page cache里面或辖。操作系統(tǒng)會控制page cache里面的內(nèi)容瘾英,什么時候?qū)懟氐酱疟P。在應(yīng)用層颂暇,對應(yīng)的就是fsync函數(shù)缺谴。

我們可以指定每條消息都調(diào)用一次fsync存盤,但這會較低性能耳鸯,也增大了磁盤IO瓣赂。也可以讓操作系統(tǒng)去控制存盤。

消息的不重不漏 – Exactly Once

一個完美的消息隊列片拍,應(yīng)該做到消息的“不重不漏”煌集,這里面包含了4重語義:
消息不會重復(fù)存儲;
消息不會重復(fù)消費捌省;
消息不會丟失存儲苫纤;
消息不會丟失消費。

先說第1個:重復(fù)存儲纲缓。發(fā)送者發(fā)送一個消息之后卷拘,服務(wù)器返回超時了。那請問祝高,這條消息是存儲成功了栗弟,還是沒有呢?
要解決這個問題:發(fā)送者需要給每條消息增加一個primary key工闺,同時服務(wù)器要記錄所有發(fā)送過的消息乍赫,用于判重。很顯然陆蟆,要實現(xiàn)這個雷厂,代價很大

重復(fù)消費:上面說過了,要避免這個叠殷,消費者需要消息confirm改鲫。但同樣,會引入其他一些問題林束,比如消費完了像棘,發(fā)送confirm的時候,掛了怎么辦壶冒? 一個消息一直處于已發(fā)送缕题,但沒有confirm狀態(tài)怎么辦?

丟失存儲:這個已經(jīng)解決

丟失消費:同丟失存儲一樣依痊,需要confirm避除。

總結(jié)一下:真正做到不重不漏怎披,exactly once,是很難的瓶摆。這個需要broker凉逛、producer、consumer和業(yè)務(wù)方的協(xié)調(diào)配合群井。

在kafka里面状飞,是保證消息不漏,也就是at least once书斜。至于重復(fù)消費問題诬辈,需要業(yè)務(wù)自己去保證,比如業(yè)務(wù)加判重表荐吉。

歡迎加入QQ群:104286694

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末焙糟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子样屠,更是在濱河造成了極大的恐慌穿撮,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痪欲,死亡現(xiàn)場離奇詭異悦穿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)业踢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門栗柒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人知举,你說我怎么就攤上這事瞬沦。” “怎么了负蠕?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵蛙埂,是天一觀的道長。 經(jīng)常有香客問我遮糖,道長,這世上最難降的妖魔是什么叠赐? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任欲账,我火速辦了婚禮,結(jié)果婚禮上芭概,老公的妹妹穿的比我還像新娘赛不。我一直安慰自己,他們只是感情好罢洲,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布踢故。 她就那樣靜靜地躺著文黎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪殿较。 梳的紋絲不亂的頭發(fā)上耸峭,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機(jī)與錄音淋纲,去河邊找鬼劳闹。 笑死,一個胖子當(dāng)著我的面吹牛洽瞬,可吹牛的內(nèi)容都是我干的本涕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼伙窃,長吁一口氣:“原來是場噩夢啊……” “哼菩颖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起为障,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤位他,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后产场,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹅髓,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年京景,在試婚紗的時候發(fā)現(xiàn)自己被綠了窿冯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡确徙,死狀恐怖醒串,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鄙皇,我是刑警寧澤芜赌,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站伴逸,受9級特大地震影響缠沈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜错蝴,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一洲愤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧顷锰,春花似錦柬赐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽州藕。三九已至,卻和暖如春酝陈,著一層夾襖步出監(jiān)牢的瞬間床玻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工后添, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留笨枯,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓遇西,卻偏偏與公主長得像馅精,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子粱檀,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容