為什么使用消息隊列?
解耦
:- 我以我的一段開發經驗舉例:
【Kafka】登錄日志處理的三次階梯式優化實踐:從同步寫入到Kafka多分區批處理
我做過一個登錄日志邏輯
,就是在登錄邏輯末尾,加一段寫進數據庫登錄日志表的邏輯,那么,如果我們啥都不用,就單純同步寫入(就是在login函數返回之前加一段寫入邏輯),那么會拖慢login的操作,在高并發時,影響系統性能。- 如果我們把寫入邏輯異步實現,就可以形成,
先讓用戶感知到登錄成功的效果,對登錄日志的寫入我們放到后臺執行
,這樣的解耦形式。 - 那就要用到消息隊列了,這時,簡單一點,我們可以利用go的channl,快速搭建一個簡易的消息隊列,但是你自己寫的很多功能是比不上kafka好使的,所以用kafka作為消息隊列,是更好的選擇。
Kafka 是基于主題(Topic)進行
消息發布與訂閱
的模型,實現生產者和消費者之間松散耦合
削峰填谷
- 應對突發流量(如秒殺活動)時,消息隊列可暫存超出下游服務處理能力的請求,下游服務按自身節奏消費,避免因流量峰值直接壓垮系統。
異步處理
- 將非核心流程(如通知推送、數據統計)從主流程中剝離,通過消息隊列異步處理,減少主流程阻塞
容錯與持久化
- 應對分布式系統中,某個上游節點/服務宕機時,下游服務來不及處理其最后發出的數據,導致數據丟失的情況。
- 如果有一個隊列做緩沖,那么他宕機前的所有發出數據,都可以暫存在這個隊列中,等候處理。
數據流處理與實時分析
(萬流歸中)- 將多個服務產生的數據匯聚,方便實時分析
廣播與多訂閱模式
- 一個生產者發布的消息需要被多個消費者處理,單純的點對點模式無法滿足,需要kafka的訂閱發布模式
設置多個消費者組,每個消費者組中的一個消費者,可以分別處理一次消息
解耦、削峰填谷、異步處理、容錯與持久化、數據流處理與實時分析、廣播與多訂閱模式
Kafka 架構原理
1. Broker
- Broker 是 Kafka 的服務器實例,負責接收、存儲和傳輸消息。Kafka 集群由一個或多個 Broker 組成,通常每個 Broker 是一個獨立的物理或虛擬服務器。
- 每個 Broker 都可以存儲多個
Topic
的數據,并為這些數據提供讀取服務。Kafka 的分布式特性允許將數據分布在不同的 Broker 上,以實現水平擴展。
2. Producer(生產者)
- Producer 是消息的發送者,負責將數據發布到 Kafka 中的
Topic
。 - Producer 可以指定消息發送到某個
Partition
,也可以通過輪詢或基于特定規則(如基于鍵的哈希值)來選擇 Partition。
3. Consumer(消費者)
- Consumer 是消息的讀取者,負責從 Kafka 的
Topic
中訂閱并讀取消息。 - Consumer 組的概念允許多個 Consumer 實例組成一個組,來平衡消費不同
Partition
的消息。
4. Topic 和 Partition
- Topic 是 Kafka 中消息的分類單位,類似于數據庫中的表。每個
Topic
可以有多個Partition
,而Partition
是 Kafka 的并行處理單位。 - 每個
Partition
是一個有序的、不可變的日志文件,每條消息在Partition
中都有一個唯一的偏移量(offset)。Partition
保證了順序性,而不同Partition
之間可以并行處理。
5. Zookeeper / Raft(控制器)
- Kafka 使用 Zookeeper 來管理集群元數據、協調 Broker 和維護
Partition
的 Leader 選舉過程。 - 新的 Kafka 版本可以使用 KRaft 協議來替代 Zookeeper,用于集群控制和選舉 Leader。
關于Raft協議:
是一種針對主節點宕機時,從節點如何快速選舉出新主節點
的分布式一致性協議。核心解決3個問題:
- 從節點如何快速感知主節點宕機?
- 主節點會定期廣播心跳,從節點接收后重置自身內置計數器。
- 若計數器歸零前未收到心跳,從節點即判定主節點宕機。
- 如何選舉新主節點?
- 感知宕機的從節點切換為候選者,向所有節點廣播
"請求投票"
。- 其他節點收到后進行投票,獲得大多數節點支持的候選者成為新主節點。
- 為什么從節點計數器要設為隨機值,而非統一短時間?
- 若計數器統一,所有從節點會同時感知宕機并爭當候選者,導致投票分散,需多次重試,降低效率。
- 隨機計數器可讓部分節點更早發起選舉,優先獲得多數票,實現快速選主。
補充
- 任期(Term):每個選舉周期的唯一標識(遞增整數),確保每次選舉在新任期內進行,避免舊投票干擾。
- 投票原則:
一任期內僅投一票
,且優先給日志更新的候選者投票(保證數據一致性)。
6. Leader 和 Follower
Kafka 通過 Partition 副本機制 來
保證數據的高可用性
。
副本機制:每個 partition 有多份
replica
,分為Leader 和 Follower
。
(且副本的數量不能大于Broker的數量,follower和leader絕對是在不同的機器
,同一機器對同一個分區也只可能存放一個副本
(包括自己)。)
- 每個
Partition
都有一個 Leader,負責處理所有的讀寫請求。其他副本稱為 Follower,它們從 Leader 同步數據。
圍繞leader和follower,可以引出
三種ACK機制
,在配置partition時,分別決定了不同級別的消息可靠性
acks=0
:Producer 只管發 不管 Broker 是否收到 → 可能丟acks=1
:Producer 發消息,Leader 收到即確認收到 → Leader 崩了可能丟acks=all
:Producer 發消息,Leader 和 Follower 都收到消息,Leader 確認消息發送成功 → Broker 崩了可能丟
- 當 Leader 宕機時,Kafka 會自動從 Follower 中選舉新的 Leader,以保證高可用性。
Kafka 的分區 Leader 選舉機制不使用 Raft 協議,在kafka 2.8之前是依賴 ZooKeeper 進行協調,kafka 2.8之后
即 Kafka Raft 協議
Raft協議 適用于單個集群的一致性管理,而 KRaft 是針對 Kafka 分區副本的分布式場景優化,支持多分區并行選舉
。
進一步梳理:
一、再次理解核心概念的正確關系
-
Topic(主題)
- Topic 是 Kafka 中消息的邏輯分類,類似于一個消息隊列的名稱。例如,電商系統中可能有“訂單消息”“支付消息”等多個 Topic,分別存儲不同類型的消息。
- Topic 本身并不直接存儲消息,而是通過“分區(Partition)”來分布式存儲消息。
-
Partition(分區)
- 每個 Topic 可以被劃分為多個 Partition(數量可配置),消息會被分散存儲在這些 Partition 中。
- Partition 是 Kafka 實現高吞吐量和分布式存儲的核心:
- 多個 Partition 可以分布在不同的 Broker 上(一個 Broker 可以存放多個 Partition),實現負載均衡。
- 消息在 Partition 中是有序存儲的(按寫入順序形成日志),但不同 Partition 之間的消息無序。
-
Broker(服務器節點)
- Broker 是 Kafka 集群中的單個服務器節點,負責存儲消息、處理生產者和消費者的請求。
- 一個 Kafka 集群由多個 Broker 組成,Broker 之間通過 ZooKeeper 協調管理(如集群元數據、分區副本分配等)。
- Broker 存儲的是 Partition,而不是直接存儲 Topic:一個 Topic 的多個 Partition 可以分布在多個 Broker 上,單個 Broker 也可以存放多個不同 Topic 的 Partition。
二、生產者與消費者的工作流程
-
生產者(Producer)
- 生產者向指定的 Topic 發送消息,Kafka 會根據一定的規則(如哈希、輪詢或自定義策略)將消息分配到該 Topic 的某個 Partition 中。
- 消息一旦寫入 Partition,就會被持久化(默認存盤),并生成一個唯一的偏移量(Offset)標識其在 Partition 中的位置。
從這里可以引出kafka的
高效持久化
核心機制:
0. Kafka 的 Partition 本質上是一個日志文件
(Log),消息被寫入時采用 “追加寫入” 的方式(即只能在文件末尾順序添加,不允許隨機修改或刪除)- kafka是存在磁盤的,向指定topic發消息,然后寫入partition,(在磁盤上是
順序寫入
的,眾所周知,在機械硬盤上,順序寫入速度可以媲美內存,固態更不用說了)將磁盤的性能發揮到了極致 - 由于消息只能追加在上一條消息末尾,為了適應磁盤順序寫入,引入偏移量機制,無需維護復雜的索引結構(僅通過 Offset 定位)減少了元數據開銷,進一步提升了寫入性能。
- 消費者通過記錄 Offset,知道下次從哪個位置開始消費(例如,消費完 Offset=5 的消息后,下次從 Offset=6 開始)。
- 因此 Partition 內的消息通過 Offset
天然保持有序
(這也是 Kafka 能保證 “分區內消息有序”
的核心)。 - Offset 僅在單個 Partition 內有效,不同 Partition 的 Offset 相互獨立
-
消費者(Consumer)與消費者組(Consumer Group)
- 消費者從 Topic 中讀取消息,必須指定消費的 Partition(由 Kafka 自動分配或手動指定)。
- 消費者組是多個消費者的集合,同一個消費者組內的消費者共同消費一個 Topic 的所有 Partition,且一個 Partition 只能被同一個消費者組內的一個消費者消費(避免重復消費)。
- 不同消費者組可以獨立消費同一個 Topic(彼此互不影響),例如“訂單消息”Topic 可以被“物流系統”和“數據分析系統”兩個消費者組分別消費。
三、總結關系圖(簡化)
Kafka 集群
├─ Broker 1
│ ├─ Topic A 的 Partition 0
│ └─ Topic B 的 Partition 1
├─ Broker 2
│ ├─ Topic A 的 Partition 1
│ └─ Topic C 的 Partition 0
└─ Broker 3└─ Topic B 的 Partition 0
- Topic A 包含 2 個 Partition,分別在 Broker 1 和 Broker 2 上。
- 每個 Broker 存放了不同 Topic 的 Partition。
kafka的存儲機制
-
消息持久化
- Kafka 的存儲機制主要圍繞 存在磁盤上的日志(Log) 文件來實現數據的高效存儲和讀取。每個 Partition 被存儲為一個 日志文件,
消息按照追加寫
(順序)的方式存儲到日志中。
通過 offest 順序
寫入
,順序讀取
- Kafka 的存儲機制主要圍繞 存在磁盤上的日志(Log) 文件來實現數據的高效存儲和讀取。每個 Partition 被存儲為一個 日志文件,
-
Segment分段存儲
- 每個 Partition 日志文件會進一步被
分為多個 Segment
。Segment 是物理上日志文件的一部分
,當一個 Segment達到預定大小或時間限制
時,Kafka 會關閉這個 Segment 并開始寫入下一個 Segment。
分段存儲
的方式使得 Kafka 在刪除舊消息時
不需要修改文件,只需要刪除老的 Segment
文件即可 - 每個 Partition 日志文件會進一步被
-
消息保留策略
- 基于
時間保留
:可以配置 Kafka 將 Partition中,超過設定時間的Segment刪除,例如保留 7 天的數據。 - 基于
大小保留
:可以根據每個 Partition 日志文件的大小來設置消息保留,超過設定大小后,刪除最早的 Segment。
- 基于
-
日志壓縮
- 永久保留最新版本的每個消息鍵。對于相同鍵的消息,Kafka 只保留最新的,刪除舊的。
-
零拷貝
- Kafka 在內存和網絡之間的傳輸
不經過 CPU 的數據拷貝
。減少了 CPU 和內存的開銷,極大提高了數據傳輸效率
在生產者發送消息、Broker 轉發消息以及消費者消費消息時都能發揮作用
- Kafka 在內存和網絡之間的傳輸
Kafka的消費者策略
- 單獨消費者模式
- 單個消費者獨占訂閱的Topic分區,其他消費者無法同時消費這些分區。
- 消費者組模式
- 多個消費者組成一個組,共同消費同一Topic,每個分區僅被組內一個消費者處理。
- 不同消費組可獨立消費同一Topic,互不干擾。
- 通過動態增減消費者實例,
靈活擴展處理能力
,提升效率。
一、Kafka 確保一致性的核心機制
- 副本同步機制:每個分區的 Follower 副本實時從 Leader 副本同步數據,保證副本間數據一致。
- ISR 列表管理:僅維護與 Leader 數據同步的副本(In-Sync Replicas),非 ISR 副本不參與數據確認,避免數據不一致。
- Leader 唯一寫入:所有讀寫操作由 Leader 副本處理,Follower 僅同步數據,確保同一分區的數據操作入口唯一。
- 選舉規則約束:Leader 宕機時,僅從 ISR 中選舉新 Leader,保證新 Leader 擁有最新數據,避免數據回退。
二、Kafka 確保可靠性的核心機制
- 持久化存儲:消息按順序寫入磁盤并生成 Offset,即使 Broker 宕機,數據也不會丟失。
- 生產者確認機制:通過
acks
參數控制消息確認級別(如acks=-1
要求 ISR 中多數副本確認),確保消息被安全存儲。 - 副本冗余:多副本機制實現數據冗余,單個副本故障時,其他副本可繼續提供服務。
- 消費者 Offset 管理:消費者通過提交 Offset 記錄消費進度,支持手動提交,避免重復消費或數據丟失。
- 故障自動恢復:Leader 故障后自動從 ISR 選舉新 Leader,Broker 故障恢復后可重新加入集群同步數據。