Kafka 是什么?你想知道嗎?
你是一個程序員,假設(shè)你維護了兩個服務(wù) A 和 B。B 服務(wù)每秒只能處理 100 個消息,但 A 服務(wù)卻每秒發(fā)出 200 個消息,B 服務(wù)哪里頂?shù)米?,分分鐘被壓垮。那么問題就來了,有沒有辦法讓 B 在不被壓垮的同時,還能處理掉 A 的消息?當然有,沒有什么是加一層中間層不能解決的,如果有,那就再加一層。這次我們要加的中間層是消息隊列 Kafka。
Kafka
什么是消息隊列
為了保護 B 服務(wù),我們很容易想到可以在 B 服務(wù)的內(nèi)存中加入一個隊列。
消息隊列在B進程里
說白了,它其實是個鏈表,鏈表的每個節(jié)點就是一個消息。每個節(jié)點有一個序號,我們叫它 Offset,記錄消息的位置。B 服務(wù)依據(jù)自己的處理能力,消費鏈表里的消息。能處理多少是多少,不斷更新已處理 Offset 的值。
Offset是什么
但這有個問題,來不及處理的消息會堆積在內(nèi)存里,如果 B 服務(wù)更新重啟,這些消息就都丟了。這個好解決,將隊列挪出來,變成一個單獨的進程。就算 B 服務(wù)重啟,也不會影響到了隊列里的消息。
消息隊列單獨一個進程
這樣一個簡陋的隊列進程,其實就是所謂的消息隊列。而像 A 服務(wù)這樣負責發(fā)數(shù)據(jù)到消息隊列的角色,就是生產(chǎn)者,像 B 服務(wù)這樣處理消息的角色,就是消費者。
生產(chǎn)者和消費者
但這個消息隊列屬實過于簡陋,像什么高性能,高擴展性,高可用,它是一個都不沾。我們來看下怎么優(yōu)化它。
高性能
B 服務(wù)由于性能較差,消息隊列里會不斷堆積數(shù)據(jù),為了提升性能,我們可以擴展更多的消費者, 這樣消費速度就上去了,相對的我們就可以增加更多生產(chǎn)者,提升消息隊列的吞吐量。
增加生產(chǎn)者和消費者
隨著生產(chǎn)者和消費者都變多,我們會發(fā)現(xiàn)它們會同時爭搶同一個消息隊列,搶不到的一方就得等待,這不純純浪費時間嗎!有解決方案嗎?有!首先是對消息進行分類,每一類是一個 topic,然后根據(jù) topic 新增隊列的數(shù)量,生產(chǎn)者將數(shù)據(jù)按 topic 投遞到不同的隊列中,消費者則根據(jù)需要訂閱不同的 topic。這就大大降低了 topic 隊列的壓力。
多個topic
但單個 topic 的消息還是可能過多,我們可以將單個隊列,拆成好幾段,每段就是一個 partition分區(qū),每個消費者負責一個 partition。這就大大降低了爭搶,提升了消息隊列的性能。
partition
高擴展性
隨著 partition 變多,如果 partition 都在同一臺機器上的話,就會導致單機 cpu 和內(nèi)存過高,影響整體系統(tǒng)性能。
于是我們可以申請更多的機器,將 partition 分散部署在多臺機器上,這每一臺機器,就代表一個 broker。我們可以通過增加 broker 緩解機器 cpu 過高帶來的性能問題。
broker
高可用
到這里,其實還有個問題,如果其中一個 partition 所在的 broker 掛了,那 broker 里所有 partition 的消息就都沒了。這高可用還從何談起?有解決方案嗎?有,連你喜歡的女生都知道手機里多聊幾個沸羊羊,你卻不知道要給 partition 加備胎嗎?我們可以給 partition 多加幾個副本,也就是 replicas,將它們分為 Leader 和 Follower。Leader 負責應(yīng)付生產(chǎn)者和消費者的讀寫請求,而 Follower 只管同步 Leader 的消息。
replicas
將 Leader 和 Follower 分散到不同的 broker 上,這樣 Leader 所在的 broker 掛了,也不會影響到 Follower 所在的 broker, 并且還能從 Follower 中選舉出一個新的 Leader partition 頂上。這樣就保證了消息隊列的高可用。
高可用
持久化和過期策略
剛剛提到的是幾個 broker 掛掉的情況,那搞大點,假設(shè)所有 broker 都掛了,那豈不是數(shù)據(jù)全丟了?為了解決這個問題,我們不能光把數(shù)據(jù)放內(nèi)存里,還要持久化到磁盤中,這樣哪怕全部 broker 都掛了,數(shù)據(jù)也不會全丟,重啟服務(wù)后,也能從磁盤里讀出數(shù)據(jù),繼續(xù)工作。
持久化
但問題又來了,磁盤總是有限的,這一直往里寫數(shù)據(jù)遲早有一天得炸。所以我們還可以給數(shù)據(jù)加上保留策略,也就是所謂的 retention policy,比如磁盤數(shù)據(jù)超過一定大小或消息放置超過一定時間就會被清理掉。
consumer group
到這里,這個消息隊列好像就挺完美了。但其實還有個問題,按現(xiàn)在的消費方式,每次新增的消費者只能跟著最新的消費 Offset 接著消費。如果我想讓新增的消費者從某個 Offset 開始消費呢?聽起來這個需求很刁鉆?我舉個例子你就明白了。
哪怕 B 服務(wù)有多個實例,但本質(zhì)上,它只有一個消費業(yè)務(wù)方,新增實例一般也是接著之前的 offset 繼續(xù)消費。假設(shè)現(xiàn)在來了個新的業(yè)務(wù)方,C 服務(wù),它想從頭開始消費消息隊列里的數(shù)據(jù),這時候就不能跟在 B 服務(wù)的 offset 后邊繼續(xù)消費了。
所以我們還可以給消息隊列加入消費者組(consumer group)的概念,B 和 C 服務(wù)各自是一個獨立的消費者組,不同消費者組維護自己的消費進度,互不打攪。
消費者組互相獨立
ZooKeeper
相信你也發(fā)現(xiàn)了,組件太多了,而且每個組件都有自己的數(shù)據(jù)和狀態(tài),所以還需要有個組件去統(tǒng)一維護這些組件的狀態(tài)信息,于是我們引入 ZooKeeper 組件。它會定期和 broker 通信,獲取 整個 kafka 集群的狀態(tài),以此判斷 某些 broker 是不是跪了,某些消費組消費到哪了。
加入ZooKeeper
Kafka 是什么
好了,到這里,當初那個簡陋的消息隊列,就成了一個高性能,高擴展性,高可用,支持持久化的超強消息隊列,沒錯,它就是我們常說的消息隊列 Kafka,上面涉及到各種概念,比如 partition 和 broker 什么的,都出自它。
Kafka是什么
kafka 的應(yīng)用場景
消息隊列是架構(gòu)中最常見的中間件之一,使用場景之多,堪稱萬金油!比如上游流量忽高忽低,想要削峰填谷,提升 cpu/gpu 利用率,用它。又比如系統(tǒng)過大,消息流向盤根錯節(jié),想要拆解組件,降低系統(tǒng)耦合,還是用它。再比如秒殺活動,請求激增,想要保護服務(wù)的同時又盡量不影響用戶,還得用它。當然,凡事無絕對,方案還得根據(jù)實際情況來定,做架構(gòu)做到最后,都是在做折中。
Kafka的應(yīng)用場景
總結(jié)
- ? kafka 是消息隊列,像消息隊列投遞消息的是生產(chǎn)者,消費消息的是消費者。增加生產(chǎn)者和消費者的實例個數(shù)可以提升系統(tǒng)吞吐。多個消費者可以組成一個消費者組,不同消費者組維護自己的消費進度,互不打攪。
- ? kafka 將消息分為多個 topic,每個 topic 內(nèi)部拆分為多個 partition,每個 partition 又有自己的副本,不同的 partition 會分布在不同的 broker 上,提升性能的同時,還增加了系統(tǒng)可用性和可擴展性。