- 消息中間件的應用場景
- 主流 MQ 架構及對比
- 說明
- Kafka 優點
- Kafka 缺點
- RocketMQ
- Pulsar
- 發展趨勢
- 各公司發展
- Kafka
- Kafka 是什麼?
- Kafka 術語
- Kafka 如何持久化?
- Kafka 檔案存儲機制
- 分區
- 為什麼分區?
- 分區政策?
- Kafka 是否會消息丢失?
- 控制器
- 控制器如何選舉?
- 控制器有什麼用?
- 控制器故障轉移
- Kafka 的 ZooKeeper 存儲結構
- 分布式事務的應用場景
- 兩階段最終一緻
- 如何保證最終一緻?
- 消息發送的一緻性如何保證?
- 發送異常會如何?
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICMyYTMvw1dvwlMvwlM3VWaWV2Zh1Wa-cmbw5icnNDOotWevpGOvwlM3MDM4QzMtUGall3LcVmdhNXLwRHdo9CXt92YucWbpRWdvx2Yx5yazF2Lc9CX6MHc0RHaiojIsJye.png)
消息中間件的應用場景
- 異步解耦
- 削峰填谷
- 順序收發
- 分布式事務一緻性
騰訊應用案例:
主流 MQ 架構及對比
說明
- Kafka:整個行業應用廣泛
- RocketMQ:阿裡,從 apache 孵化
- Pulsar:雅虎開源,符合雲原生架構的消息隊列,社群活躍
- RabbitMQ 架構比較老,AMQP并沒有在主流的 MQ 得到支援
- NSQ:記憶體型,不是最優選擇
- ActiveMQ、ZeroMQ 可忽略
Kafka 優點
- 非常成熟,生态豐富,與 Hadoop 連接配接緊密
- 吞吐非常高,可用性高
- sharding
- 提升 replication 速度
- 主要功能:pub-sub,壓縮支援良好
- 可按照 at least once, at most once 進行配置使用,exactly once 需要 Consumer 配合
- 叢集部署簡單,但 controller 邏輯很複雜,實作partition 的多副本、資料一緻性
- controller 依賴 ZooKeeper
- 異步刷磁盤(除了錢的業務,很少有同步 flush 的需求)
Kafka 缺點
- 寫入延時穩定性問題,partition 很多時
- Kafka 通常用機械盤,随機寫造成吞吐下降和延時上升
- 100ms ~ 500ms
- 運維的複雜性
- 單機故障後補充副本
- 資料遷移
- 快手的優化:遷移 partition 時舊資料不動,新資料寫入新 partition 一定時間後直接切換
RocketMQ
- 阿裡根據 Kafka 改造适應電商等線上業務場景
- 以犧牲性能為代價增強功能
- 按 key 對消息查詢,維護 hash 表,影響 io
- 為了在多 shard 場景下保證寫入延遲穩定,在 broker 級别将所有 shard 目前寫入的資料放入一個檔案,形成 commitlog list,放若幹個 index 檔案維護邏輯 topic 資訊,造成更多的随機讀
- 沒有中心管理節點,現在看起來并沒有什麼用,中繼資料并不多
- 高精度的延遲消息(快手已支援秒級精度的延遲消息)
Pulsar
- 存儲、計算分離,友善擴容
- 存儲:bookkeeper
- MQ邏輯:無狀态的 broker 處理
發展趨勢
- 雲原生
- 批流一體:跑任務時,需要先把 Kafka 資料→HDFS,資源消耗大。如果本來就存在 HDFS,能節省很大資源
- Serverless
各公司發展
- 快手:Kafka
- 所有場景均在使用
- 特殊形态的讀寫分離
- 資料實時消費到 HDFS
- 在有明顯 lag 的 consumer 讀取時,broker 把請求從本地磁盤轉發的 HDFS
- 不會因為有 lag 的 consumer 對日常讀寫造成明顯的磁盤随機讀寫
- 由于自己改造,社群新功能引入困難
- 阿裡巴巴:開源 RocketMQ
- 位元組跳動
- 線上場景:NSQ→RocketMQ
- 離線場景:Kafka→自研的存儲計算分類的 BMQ(協定層直接相容Kafka,使用者可以不換 client)
- 百度:自研的 BigPipe,不怎麼樣
- 美團:Kafka 架構基礎上用 Java 進行重構,内部叫 Mafka
- 騰訊:部分使用了自研的 PhxQueue,底層是 KV 系統
- 滴滴:DDMQ
- 對 RocketMQ 和 Kafka 進行封裝
- 多機房資料一緻性可能有問題
- 小米:自研 Talos
- 架構類似 pulsar,存儲是 HDFS,讀場景有優化
Kafka
- Kafka官網:https://kafka.apache.org/documentation/#uses
- 最新版本:2.7
Kafka 是什麼?
- 開源的消息引擎系統(消息隊列/消息中間件)
- 分布式流處理平台
- 釋出/訂閱模型
- 削峰填谷
Kafka 術語
- Topic:釋出訂閱的主題
- Producer:向Topic釋出消息的用戶端
- Consumer:消費者
- Consumer Group:消費者組,多個消費者共同組成一個組
- Broker:Kafka的服務程序
- Replication:備份,相同資料拷貝到多台機器
- Leader Replica
- Follower Replica,不與外界互動
- Partition:分區,解決伸縮性問題,多個Partition組成一個Topic
- Segment:partition 由多個 segment 組成
Kafka 如何持久化?
- 消息日志(Log)儲存資料,磁盤追加寫(Append-only)
- 避免緩慢的随機I/O操作
- 高吞吐
- 定期删除消息(日志段)
Kafka 檔案存儲機制
https://www.open-open.com/lib/view/open1421150566328.html
- 每個 partition 相當于一個巨型檔案→多個大小相等 segment 資料檔案中
- 每個 partition 隻需要順序讀寫就行了,segment 檔案生命周期由配置決定
- segment file 組成:
- index file:索引檔案
- data file:資料檔案
- segment file 檔案命名規則:
- 全局第一個 segment 是 0
- 後序每個加上全局 partition 的最大 offset
一對 segment file
message 實體結構
分區
為什麼分區?
- Kafka的消息組織方式:主題-分區-消息
- 一條消息,僅存在某一個分區中
- 提高伸縮性,不同分區可以放到不同機器,讀寫操作也是以分區粒度
分區政策?
- 輪詢
- 随機
- 按 key 保序,單分區有序
Kafka 是否會消息丢失?
- 隻對“已送出”的消息做有限度的持久化保證
- 已送出的消息:消息寫入日志檔案
- 有限度的持久化保證:N個 broker 至少一個存活
- 生産者丢失資料
- producer.send(msg) 異步發送消息,不保證資料到達Kafka
- producer.send(msg, callback) 判斷回調
- 消費者程式丢失資料
- 應該「先消費消息,後更新位移的順序」
- 新問題:消息的重複處理
- 多線程異步處理消息,Consumer不要開啟自動送出位移,應用程式手動送出位移
控制器
- 在 ZooKeeper幫助下管理和協調整個 Kafka 叢集
- 運作過程中,隻能有一個 Broker 成為控制器
控制器如何選舉?
在 ZooKeeper 建立 /controller 節點,第一個建立成功的 Broker 被指定為控制器。
控制器有什麼用?
- 主題管理(建立、删除、增加分區)
- 分區重配置設定
- 上司者選舉
- 叢集成員管理(新增 Broker、Broker 主動關閉、Broker 當機)(ZooKeeper 臨時節點)
- 資料服務:最全的叢集中繼資料資訊
控制器故障轉移
- 隻有一個 Broker 當控制器,單點失效,立即啟用備用控制器
Kafka 的 ZooKeeper 存儲結構
分布式事務的應用場景
- 團隊内部,某些操作要同時更新多個資料源
- 業務團隊 A 完成某個操作後,B 業務的某個操作也必須完成,A 業務并不能直接通路 B 的資料庫
- 公司之間,使用者付款後,支付系統(支付寶/微信)必須通知商家的系統更新訂單狀态
兩階段最終一緻
- 先完成資料源 A 的事務(一階段)
- 成功後通過某種機制,保證資料源 B 的事務(二階段)也一定最終完成
- 不成功,會不斷重試直到成功為止
- 或達到一定重試次數後停止(配合對賬、人工處理)
如何保證最終一緻?
為了保證最終一緻,消息系統和業務程式需要保證:
- 消息發送的一緻性:消息發送時,一階段事務和消息發送必須同時成功或失敗
- 消息存儲不丢失:消息發送成功後,到消息被成功消費前,消息伺服器(broker)必須存儲好消息,保證發生故障時,消息不丢失
- 消費者不丢失消息:處理失敗不丢棄,重試直到成功為止
消息發送的一緻性如何保證?
目标
:本地事務、消息發送必須同時成功/失敗
問題
- 先執行本地事務,再發送消息,消息可能發送失敗
- 可把失敗的消息放入記憶體,稍後重試,但成功率也無法達到 100%
解決方案`* 先發送半消息(Half Msg,類似 Prepare 操作),不會投遞給消費者
- 半消息發送成功,再執行 DB 操作
- DB 操作執行成功後,送出半消息
發送異常會如何?
- 1 異常,半消息發送失敗,本地 DB 沒有執行,整個操作失敗,DB/消息的狀态一緻(都沒有送出)
- 2 異常/逾時
- 生産者以為失敗了,不執行 DB
- broker 存儲半消息成功,等不到後序操作,會詢問生産者是送出還是復原(第6步)
- 3 DB操作失敗:生産者在第 4 步告知 broker 復原半消息
- 4 送出/復原半消息失敗:broker 等不到這個操作,觸發回查(第 6 步)
- 5、6、7回查失敗:RocketMQ 最多回查 15 次
代碼、思維導圖筆記下載下傳
代碼和思維導圖在 GitHub 項目中,歡迎大家 star!