一.概述
消息隊列模式:
- 點對點: 1:1。就是一個隊列只能由一個消費者進行消費,這個消費者消費完畢就把消息進行刪除,不會再給別的消費者。只能消費者拉消息。
- 發布/訂閱: 1:多
- 消息隊列主動推送消息。
- 缺點:推送速率難以適應消費速率,不知道消費者的處理效率,造成浪費。
- 消費方主動從消息隊列拉取消息。
- 缺點:消息延遲(比如每隔2秒進行拉取,就會造成2秒的延遲),每一個消費方都處於忙循環,一直檢測有沒有消息。(kafka)
- kafka改進:使用長輪詢:消費者去 Broker 拉消息,定義了一個超時時間,也就是說消費者去請求消息,如果有的話馬上返回消息,如果沒有的話消費者等着直到超時,然后再次發起拉消息請求。不會頻繁的進行拉取。
- 缺點:消息延遲(比如每隔2秒進行拉取,就會造成2秒的延遲),每一個消費方都處於忙循環,一直檢測有沒有消息。(kafka)
- 消息隊列主動推送消息。
什么是Kafka?
- 是一個分布式的基於發布訂閱模式的消息隊列,主要應用於大數據實時處理領域,天然分布式。
二.Kafka基礎架構
- Producer :消息生產者,就是向 kafka broker 發消息的客戶端;
- Consumer :消息消費者,向 kafka broker 取消息的客戶端;
- Consumer Group (CG):消費者組,由多個 consumer 組成。一個消費者組消費一個topic,消費者組的每一個消費者消費一個或多個Partition。
- Broker :一台 kafka 服務器就是一個 broker。一個集群由多個 broker 組成。一個 broker 可以容納多個 topic。
- Topic :可以理解為一個隊列,生產者和消費者面向的都是一個 topic;
- Partition:為了實現擴展性,一個非常大的 topic 可以分布到多個 broker(即服務器)上, 一個 topic 可以分為多個 partition(每個partition分布在不同的Broker上),每個 partition 是一個有序的隊列;
- Replica:副本,為保證集群中的某個節點發生故障時,該節點上的 partition 數據不丟失,且 kafka 仍然能夠繼續工作,kafka 提供了副本機制,一個 topic 的每個分區都有若干個副本, 一個 leader 和若干個 follower。
- leader:每個分區多個副本的主,生產者發送數據的對象,以及消費者消費數據的對象都是 leader。(leader和follower都是Partition,放在不同的Broker中)
- follower:每個分區多個副本中的從,實時從 leader 中同步數據,保持和 leader 數據的同步。leader 發生故障時,某個 follower 會成為新的 follower。
總結:
topic就相當於Rabbit MQ 的queue,現在把queue進行分區,分為多個Partition。並且一個節點只有一個主Partition。相當於可以把一個消息可以分在不同的機器上的不同主Partition上,最后交給一個消費者組。(也可以理解為把一個topic分為不同的主partition”縱向“放在不同的機器上)。
一個topic對應多個消費組,一個消費組可以接受多個的topic。消費者組內每個消費者負責消費不同分區的數據,一個分區只能由一個組內消費者消費。
三.如何保證消息的可靠性
要保證消息不丟失,需要三方面都進行保證:生產者(ISR,ack),消費者(offset),Kafka(持久化,集群(副本同步策略))
生產者:topic 的每個 partition 收到 producer 發送的數據后,都需要向 producer 發送 ack(acknowledgement 確認收到),如果 producer 收到 ack,就會進行下一輪的發送,否則重新發送數據。
要想每一個partition發送ack,就需要每一個partition的follower進行同步才能發送ack。
- 副本數據同步策略
- 半數以上完成同步,就發送 ack。
- 優點:延遲低;缺點:選舉新的 leader 時,容忍 n 台 節點的故障,需要 2n+1 個副本
- 全部完成同步,才發送 ack。(kafka采用)
- 優點:選舉新的 leader 時,容忍 n 台 節點的故障,需要 n+1 個副本;缺點:延遲高。
- 半數以上完成同步,就發送 ack。
- AR,ISR,OSR:AR=ISR+OSR
- ISR:In-Sync Replicas 副本同步隊列,存放可以被同步的副本,有些follower同步時超過閾值都會被剔除出ISR(萬一有的follower宕機了,不能一直等它吧),存入OSR(Outof-Sync Replicas)列表,新加入的follower也會先存放在OSR中。
- AR:所有副本
- ack應答機制:
- 0:不需要等待ack返回,容易丟失數據。
- 1:只要Leader收到數據,就進行ack。不需要等待follower都同步完成。當leader沒有同步完數據前宕機,丟失數據。
- -1:等待所有的follower都同步完,再進行ack。會造成數據重復。這時候才認為一條數據被commit了(放心了)。
消費者:由於 consumer 在消費過程中可能會出現斷電宕機等故障,consumer 恢復后,需要從故障前的位置的繼續消費,所以 consumer 需要實時記錄自己消費到了哪個 offset,以便故障恢復后繼續消費。Kafka就是用
offset
來表示消費者的消費進度到哪了,
每個消費者會都有自己的offset
。說白了
offset
就是表示消費者的
消費進度。
- 在以前版本的Kafka,這個offset是由Zookeeper來管理的,后來Kafka開發者認為Zookeeper不合適大量的刪改操作,於是把offset在broker以內部topic(
__consumer_offsets
)的方式來保存起來。 - 關閉自動提交位移,在消息被完整處理之后再手動提交位移。enable.auto.commit=false
- LEO:指的是每個副本最大的 offset;如下圖,leader最大的LEO到19,其他的follower還沒有同步完,leader掛了,有的follower只同步到12,有的同步到15,就會出現消費數據錯亂,所以讓消費者只能從HW的位置進行消費。這樣保證消費數據不會出現錯亂。
- HW:指的是消費者能見到的最大的 offset,ISR 隊列中最小的 LEO。
Kafka:Kafka是以 日志文件進行存儲。

- 采用分片機制和索引機制。
- topic是邏輯上的概念,Partition是物理上的概念。每一個Partition又分為好幾個Segment,每一個Segment存放2個文件。.log和.index文件。
- .index文件存儲索引,log文件存儲真正的消息,新的消息放在文件尾部。
四.Kafka為什么那么快?Kafka是以日志文件保存在磁盤上,但是效率還是很高,為什么呢?我們來分析它對於讀寫的優化。
- 寫數據:
- 順序寫入:Kafka把數據一直追加到文件末端,省去了大量磁頭尋址的時間。
-
Memory Mapped Files:mmf 直接利用操作系統的Page來實現文件到物理內存的映射,完成之后對物理內存的操作會直接同步到硬盤。
- 讀數據:
- 零拷貝:操作系統的文章有講。
- 批量發送:Kafka允許進行批量發送消息,producter發送消息的時候,可以將消息緩存在本地,等到了固定條件發送到 Kafka 。
- 數據壓縮:可以通過GZIP或Snappy格式對消息集合進行壓縮。壓縮的好處就是減少傳輸的數據量,減輕對網絡傳輸的壓力。
五.其他小問題
生產者分區策略:
- 指明Partition的情況下,直接存到指明的Partition值。
- 沒有指明Partition但是有key,將key的hash值與topic的partition數進行取余得到Partition值
- 輪詢:既沒有Partition又沒有key,第一次調用時隨機生成一個整數(后面每次調用在這個整數上自增),將這個值與topic可用的Partititon總數取余得到Partititon值。
消費者分區策略:
-
分配策略觸發條件:當消費者組中 消費者個數發生變化(新增消費者/某一個消費者宕機)的時候就會觸發分配策略。
- 輪詢:
- Range(默認):按照消費者組進行划分,先算topic組/消費者的個數,按照上面消費數量大的原則進行分配。
kafka中的 zookeeper 起到什么作用,可以不用zookeeper么?
- 早期版本的kafka用zk做元數據信息存儲,consumer的消費狀態,group的管理以及 offset的值等。考慮到和zk打交道網絡的問題,效率不高,就在新版本弱化了zk的依賴。
- broker依然依賴於ZK,zookeeper 在kafka中還用來選舉controller和檢測broker是否存活等等。
如果leader crash時,ISR為空怎么辦?用如下參數進行調節
- unclean.leader.election,這個參數有兩個值:
- true(默認):允許不同步副本成為leader,由於不同步副本的消息較為滯后,此時成為leader,可能會出現消息不一致的情況。
- false:不允許不同步副本成為leader,此時如果發生ISR列表為空,會一直等待舊leader恢復,降低了可用性。
為什么Kafka不支持讀寫分離?
- 在Kafka中,生產者寫入消息、消費者讀取消息的操作都是與 leader 副本進行交互的,從 而實現的是一種主寫主讀的生產消費模型。
-
Kafka 並不支持主寫從讀,因為主寫從讀有 2 個很明顯的缺點:
-
數據一致性問題。數據從主節點轉到從節點必然會有一個延時的時間窗口,這個時間 窗口會導致主從節點之間的數據不一致。某一時刻,在主節點和從節點中 A 數據的值都為 X, 之后將主節點中 A 的值修改為 Y,那么在這個變更通知到從節點之前,應用讀取從節點中的 A 數據的值並不為最新的 Y,由此便產生了數據不一致的問題。
-
延時問題。類似 Redis 這種組件,數據從寫入主節點到同步至從節點中的過程需要經 歷網絡→主節點內存→網絡→從節點內存這幾個階段,整個過程會耗費一定的時間。而在 Kafka 中,主從同步會比 Redis 更加耗時,它需要經歷網絡→主節點內存→主節點磁盤→網絡→從節 點內存→從節點磁盤這幾個階段。對延時敏感的應用而言,主寫從讀的功能並不太適用。
-
Kafka中是怎么體現消息順序性的?
- kafka每個partition中的消息在寫入時都是有序的,消費時,每個partition只能被每一個group中的一個消費者消費,保證了消費時也是有序的。
- 整個topic不保證有序。如果為了保證topic整個有序,那么將partition調整為1.
Kafka中最少一次/最多一次/精准一次?
- at lest once(最少一次):消息不丟,但可能重復。先處理消息,再保存offset。當處理消息的時候宕機,沒有存offset,重新消費的時候,再次處理剛才沒有處理的消息。但是當存offset的時候宕機,再次消費的時候,重新消費到剛才的消息,造成重復。
- at most once(最多一次):消息會丟,但不會重復。先保存offset,再處理消息。當處理消息的時候宕機,由於已經保存了offset,重新消費的時候,就不會處理剛才沒有處理的消息。但是當保存offset的時候宕機,還沒有消費消息,再次消費的時候不會重復。
- Exactly Once(精准一次):消息不丟,也不重復。
- 解決方案一:最少一次+冪等性
- 解決方案二:最多一次+事務
寄語:要偷偷的努力,希望自己也能成為別人的夢想。