本文為小馬閱讀《再談基于 Kafka 和 ZooKeeper 的分布式消息隊(duì)列原理》一文的筆記摘要記錄臊恋,便于日后復(fù)盤逆甜,非原創(chuàng)內(nèi)容虱肄。版權(quán)歸原作者所有,轉(zhuǎn)載請注明出處交煞,侵刪咏窿。
小馬最近學(xué)習(xí)了《深入理解kafka 核心設(shè)計(jì)與實(shí)踐原理》朱忠華 著 一書,機(jī)緣巧合中又看到了這篇文章素征,覺得整理得很是詳細(xì)和全面集嵌,圖文并茂很直觀,在此摘錄御毅。
精華總結(jié):依靠主題分區(qū)來類似分庫分表的方式提高性能根欧,用副本主從同步+ISR(偏移量和HW)來保證消息隊(duì)列的可靠性,消費(fèi)者提交消費(fèi)位移來保證消息不丟失和重復(fù)消費(fèi)等端蛆,用ZK來處理服務(wù)發(fā)現(xiàn)凤粗,負(fù)載均衡,選舉今豆,集群管理嫌拣,消費(fèi)位移記錄(以被推薦記錄于kafka主題內(nèi))等。
HW之前的消息才能被消費(fèi)者拉取呆躲,理解為都同步備份完了异逐,才算生產(chǎn)者消息提交成功,對消費(fèi)者可見插掂。這種ISR機(jī)制影響了性能但是保證了可靠性灰瞻,保證消息不丟失。消費(fèi)位移提交燥筷,默認(rèn)的是自動提交箩祥,異常下消息會重復(fù)消費(fèi)會丟失,但可以參數(shù)配置手動提交肆氓,自行在業(yè)務(wù)處理完再提交袍祖。消費(fèi)者拉的方式自主獲取消費(fèi),便于消費(fèi)者自行控制消費(fèi)速率谢揪。默認(rèn)分區(qū)規(guī)則是哈希一致性方式蕉陋。
相比Redis消息隊(duì)列本身的可靠性就不如,被消費(fèi)者拉取完就認(rèn)為消費(fèi)完了拨扶,消息丟失凳鬓,所以一般需要自行維護(hù)ack機(jī)制。
Kafka的消息是保存或緩存在磁盤上的患民,一般認(rèn)為在磁盤上讀寫數(shù)據(jù)是會降低性能的缩举,因?yàn)閷ぶ窌容^消耗時間,但是實(shí)際上,Kafka的特性之一就是高吞吐率仅孩。即使是普通的服務(wù)器托猩,Kafka也可以輕松支持每秒百萬級的寫入請求,超過了大部分的消息中間件辽慕,這種特性也使得Kafka在日志處理等海量數(shù)據(jù)場景廣泛應(yīng)用京腥。Kafka速度的秘訣在于,它把所有的消息都變成一個批量的文件溅蛉,并且進(jìn)行合理的批量壓縮公浪,減少網(wǎng)絡(luò)IO損耗,通過mmap提高I/O速度船侧,寫入數(shù)據(jù)的時候由于單個Partion是末尾添加所以速度最優(yōu)欠气;讀取數(shù)據(jù)的時候配合sendfile直接暴力輸出。
1镜撩、Kafka 總體架構(gòu)
一個典型的 Kafka 體系架構(gòu)包括若干 Producer(消息生產(chǎn)者)晃琳,若干 broker(作為 Kafka 節(jié)點(diǎn)的服務(wù)器),若干 Consumer(Group)琐鲁,以及一個 ZooKeeper 集群。Kafka通過 ZooKeeper 管理集群配置人灼、選舉 Leader 以及在 consumer group 發(fā)生變化時進(jìn)行 Rebalance(即消費(fèi)者負(fù)載均衡围段,在下一課介紹)。Producer 使用 push(推)模式將消息發(fā)布到 broker投放,Consumer 使用 pull(拉)模式從 broker 訂閱并消費(fèi)消息奈泪。
Kafka 節(jié)點(diǎn)的 broker涉及 Topic、Partition 兩個重要概念
在 Kafka 架構(gòu)中灸芳,有幾個術(shù)語:
Producer:生產(chǎn)者涝桅,即消息發(fā)送者,push 消息到 Kafka 集群中的 broker(就是 server)中烙样;
Broker:Kafka 集群由多個 Kafka 實(shí)例(server) 組成冯遂,每個實(shí)例構(gòu)成一個 broker,說白了就是服務(wù)器谒获;
Topic:producer 向 kafka 集群 push 的消息會被歸于某一類別蛤肌,即Topic,這本質(zhì)上只是一個邏輯概念批狱,面向的對象是 producer 和 consumer裸准,producer 只需要關(guān)注將消息 push 到哪一個 Topic 中,而 consumer 只需要關(guān)心自己訂閱了哪個 Topic赔硫;
Partition:每一個 Topic 又被分為多個 Partitions炒俱,即物理分區(qū);出于負(fù)載均衡的考慮,同一個 Topic 的 Partitions 分別存儲于 Kafka 集群的多個 broker 上权悟;而為了提高可靠性砸王,這些 Partitions 可以由 Kafka 機(jī)制中的 replicas 來設(shè)置備份的數(shù)量;如上面的框架圖所示僵芹,每個 partition 都存在兩個備份处硬;
Consumer:消費(fèi)者,從 Kafka 集群的 broker 中 pull 消息拇派、消費(fèi)消息荷辕;
Consumer group:high-level consumer API 中,每個 consumer 都屬于一個 consumer-group件豌,每條消息只能被 consumer-group 中的一個 Consumer 消費(fèi)疮方,但可以被多個 consumer-group 消費(fèi);
replicas:partition 的副本茧彤,保障 partition 的高可用骡显;
leader:replicas 中的一個角色, producer 和 consumer 只跟 leader 交互曾掂;
follower:replicas 中的一個角色惫谤,從 leader 中復(fù)制數(shù)據(jù),作為副本珠洗,一旦 leader 掛掉溜歪,會從它的 followers 中選舉出一個新的 leader 繼續(xù)提供服務(wù);
controller:Kafka 集群中的其中一個服務(wù)器许蓖,用來進(jìn)行 leader election 以及 各種 failover蝴猪;
ZooKeeper:Kafka 通過 ZooKeeper 來存儲集群的 meta 信息等,文中將詳述膊爪。
2自阱、主題和分區(qū),偏移量
一個 topic 可以認(rèn)為是一類消息米酬,每個 topic 將被分成多個 partition沛豌,每個 partition 在存儲層面是 append log 文件。任何發(fā)布到此 partition 的消息都會被追加到log文件的尾部赃额,每條消息在文件中的位置稱為 offset(偏移量)琼懊,offset 為一個 long 型的數(shù)字,它唯一標(biāo)記一條消息爬早。 Kafka 機(jī)制中哼丈,producer push 來的消息是追加(append)到 partition 中的,這是一種順序?qū)懘疟P的機(jī)制筛严,效率遠(yuǎn)高于隨機(jī)寫內(nèi)存醉旦,如下示意圖:
3、副本復(fù)制原理和同步方式
Kafka 中 topic 的每個 partition 有一個預(yù)寫式的日志文件,雖然 partition 可以繼續(xù)細(xì)分為若干個 segment 文件车胡,但是對于上層應(yīng)用來說檬输,仍然可以將 partition 看成最小的存儲單元(一個有多個 segment 文件拼接的 “巨型” 文件),每個 partition 都由一些列有序的匈棘、不可變的消息組成丧慈,這些消息被連續(xù)的追加到 partition 中。
上圖中有兩個新名詞:HW 和 LEO主卫。這里先介紹下 LEO逃默,LogEndOffset 的縮寫,表示每個 partition 的 log 最后一條 Message 的位置簇搅。HW 是 HighWatermark 的縮寫完域,是指 consumer 能夠看到的此 partition 的位置,這個涉及到多副本的概念瘩将,這里先提及一下吟税,下文再詳述。
言歸正傳姿现,為了提高消息的可靠性肠仪,Kafka 每個 topic 的 partition 有 N 個副本(replicas),其中 N(大于等于 1)是 topic 的復(fù)制因子(replica fator)的個數(shù)备典。Kafka 通過多副本機(jī)制實(shí)現(xiàn)故障自動轉(zhuǎn)移藤韵,當(dāng) Kafka 集群中出現(xiàn) broker 失效時,副本機(jī)制可保證服務(wù)可用熊经。對于任何一個 partition,它的 N 個 replicas 中欲险,其中一個 replica 為 leader镐依,其他都為 follower,leader 負(fù)責(zé)處理 partition 的所有讀寫請求天试,follower 則負(fù)責(zé)被動地去復(fù)制 leader 上的數(shù)據(jù)槐壳。如下圖所示,Kafka 集群中有 4 個 broker喜每,某 topic 有 3 個 partition务唐,且復(fù)制因子即副本個數(shù)也為 3:
如果 leader 所在的 broker 發(fā)生故障或宕機(jī),對應(yīng) partition 將因無 leader 而不能處理客戶端請求带兜,這時副本的作用就體現(xiàn)出來了:一個新 leader 將從 follower 中被選舉出來并繼續(xù)處理客戶端的請求枫笛。
4、同步副本 ISR
上一節(jié)中講到了同步副本隊(duì)列 ISR(In-Sync Replicas)刚照。雖然副本極大的增強(qiáng)了可用性刑巧,但是副本數(shù)量對 Kafka 的吞吐率有一定影響。默認(rèn)情況下 Kafka 的 replica 數(shù)量為 1,即每個 partition 都只有唯一的 leader啊楚,無 follower吠冤,沒有容災(zāi)能力。為了確保消息的可靠性恭理,生產(chǎn)環(huán)境中拯辙,通常將其值(由 broker 的參數(shù) offsets.topic.replication.factor 指定)大小設(shè)置為大于 1,比如 3颜价。 所有的副本(replicas)統(tǒng)稱為 Assigned Replicas涯保,即 AR。ISR 是 AR 中的一個子集拍嵌,由 leader 維護(hù) ISR 列表遭赂,follower 從 leader 同步數(shù)據(jù)有一些延遲(由參數(shù) replica.lag.time.max.ms 設(shè)置超時閾值),超過閾值的 follower 將被剔除出 ISR横辆, 存入 OSR(Outof-Sync Replicas)列表撇他,新加入的 follower 也會先存放在 OSR 中。AR=ISR+OSR狈蚤。
上面一節(jié)還涉及到一個概念困肩,即 HW。HW 俗稱高水位脆侮,HighWatermark 的縮寫锌畸,取一個 partition 對應(yīng)的 ISR 中最小的 LEO 作為 HW,consumer 最多只能消費(fèi)到 HW 所在的位置靖避。另外每個 replica 都有 HW潭枣,leader 和 follower 各自負(fù)責(zé)更新自己的 HW 的狀態(tài)。對于 leader 新寫入的消息幻捏,consumer 不能立刻消費(fèi)盆犁,leader 會等待該消息被所有 ISR 中的 replicas 同步后更新 HW,此時消息才能被 consumer 消費(fèi)篡九。這樣就保證了如果 leader 所在的 broker 失效谐岁,該消息仍然可以從新選舉的 leader 中獲取。對于來自內(nèi)部 broker 的讀取請求榛臼,沒有 HW 的限制伊佃。
下圖詳細(xì)的說明了當(dāng) producer 生產(chǎn)消息至 broker 后,ISR 以及 HW 和 LEO 的流轉(zhuǎn)過程:
由此可見沛善,Kafka 的復(fù)制機(jī)制既不是完全的同步復(fù)制航揉,也不是單純的異步復(fù)制。事實(shí)上金刁,同步復(fù)制要求所有能工作的 follower 都復(fù)制完迷捧,這條消息才會被 commit织咧,這種復(fù)制方式受限于復(fù)制最慢的 follower,會極大的影響吞吐率漠秋。而異步復(fù)制方式下笙蒙,follower 異步的從 leader 復(fù)制數(shù)據(jù),數(shù)據(jù)只要被 leader 寫入 log 就被認(rèn)為已經(jīng) commit庆锦,這種情況下如果 follower 都還沒有復(fù)制完捅位,落后于 leader 時,突然 leader 宕機(jī)搂抒,則會丟失數(shù)據(jù)艇搀,降低可靠性。而 Kafka 使用 ISR 的策略則在可靠性和吞吐率方面取得了較好的平衡求晶。
Kafka 的 ISR 的管理最終都會反饋到 ZooKeeper 節(jié)點(diǎn)上焰雕,具體位置為:
/brokers/topics/[topic]/partitions/[partition]/state
目前,有兩個地方會對這個 ZooKeeper 的節(jié)點(diǎn)進(jìn)行維護(hù)芳杏。
Controller 來維護(hù):Kafka 集群中的其中一個 Broker 會被選舉為 Controller矩屁,主要負(fù)責(zé) Partition 管理和副本狀態(tài)管理,也會執(zhí)行類似于重分配 partition 之類的管理任務(wù)爵赵。在符合某些特定條件下吝秕,Controller 下的 LeaderSelector 會選舉新的 leader,ISR 和新的 leader_epoch 及 controller_epoch 寫入 ZooKeeper 的相關(guān)節(jié)點(diǎn)中空幻。同時發(fā)起 LeaderAndIsrRequest 通知所有的 replicas烁峭。
leader 來維護(hù):leader 有單獨(dú)的線程定期檢測 ISR 中 follower 是否脫離 ISR,如果發(fā)現(xiàn) ISR 變化秕铛,則會將新的 ISR 的信息返回到 ZooKeeper 的相關(guān)節(jié)點(diǎn)中约郁。
5、深入解讀 HW 機(jī)制
考慮這樣一種場景:acks=-1但两,部分 ISR 副本完成同步鬓梅,此時leader掛掉,如下圖所示:follower1 同步了消息 4镜遣、5,follower2 同步了消息 4士袄,與此同時 follower2 被選舉為 leader悲关,那么此時 follower1 中的多出的消息 5 該做如何處理呢?
類似于木桶原理娄柳,水位取決于最低那塊短板寓辱。
如上圖,某個 topic 的某 partition 有三個副本赤拒,分別為 A秫筏、B诱鞠、C。A 作為 leader 肯定是 LEO 最高这敬,B 緊隨其后航夺,C 機(jī)器由于配置比較低,網(wǎng)絡(luò)比較差崔涂,故而同步最慢阳掐。這個時候 A 機(jī)器宕機(jī),這時候如果 B 成為 leader冷蚂,假如沒有 HW缭保,在 A 重新恢復(fù)之后會做同步(makeFollower) 操作,在宕機(jī)時 log 文件之后直接做追加操作蝙茶,而假如 B 的 LEO 已經(jīng)達(dá)到了 A 的 LEO艺骂,會產(chǎn)生數(shù)據(jù)不一致的情況,所以使用 HW 來避免這種情況隆夯。 A 在做同步操作的時候钳恕,先將 log 文件截?cái)嗟街白约旱?HW 的位置,即 3吮廉,之后再從 B 中拉取消息進(jìn)行同步苞尝。
如果失敗的 follower 恢復(fù)過來,它首先將自己的 log 文件截?cái)嗟缴洗?checkpointed 時刻的 HW 的位置宦芦,之后再從 leader 中同步消息宙址。leader 掛掉會重新選舉,新的 leader 會發(fā)送 “指令” 讓其余的 follower 截?cái)嘀磷陨淼?HW 的位置然后再拉取新的消息调卑。
當(dāng) ISR 中的個副本的 LEO 不一致時抡砂,如果此時 leader 掛掉,選舉新的 leader 時并不是按照 LEO 的高低進(jìn)行選舉恬涧,而是按照 ISR 中的順序選舉注益。
6、記錄消費(fèi)進(jìn)度 Offset位移
在 consumer 對指定消息 partition 的消息進(jìn)行消費(fèi)的過程中溯捆,需要定時地將 partition 消息的消費(fèi)進(jìn)度 Offset 記錄到 ZooKeeper上丑搔,以便在該 consumer 進(jìn)行重啟或者其它 consumer 重新接管該消息分區(qū)的消息消費(fèi)權(quán)后,能夠從之前的進(jìn)度開始繼續(xù)進(jìn)行消息消費(fèi)提揍。Offset 在 ZooKeeper 中由一個專門節(jié)點(diǎn)進(jìn)行記錄啤月,其節(jié)點(diǎn)路徑為:
#節(jié)點(diǎn)內(nèi)容就是Offset的值。/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id]
PS:Kafka 已推薦將 consumer 的 Offset 信息保存在 Kafka 內(nèi)部的 topic 中劳跃,即:
__consumer_offsets(/brokers/topics/__consumer_offsets)
并且默認(rèn)提供了 kafka_consumer_groups.sh 腳本供用戶查看consumer 信息(命令:sh kafka-consumer-groups.sh –bootstrap-server * –describe –group *)谎仲。在當(dāng)前版本中,offset 存儲方式要么存儲在本地文件中刨仑,要么存儲在 broker 端郑诺,具體的存儲方式取決 offset.store.method 的配置夹姥,默認(rèn)是存儲在 broker 端。
7辙诞、zookeeper
在基于 Kafka 的分布式消息隊(duì)列中辙售,ZooKeeper 的作用有:broker 注冊、topic 注冊倘要、producer 和 consumer 負(fù)載均衡圾亏、維護(hù) partition 與 consumer 的關(guān)系、記錄消息消費(fèi)的進(jìn)度以及 consumer 注冊等封拧。
參考原文: