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