1 前言
本文里面涉及到較多基礎概念,如果忘記了,那么可以去看下《一文帶你快速入門kafka》。對于一個消息中間件而言,可靠性是是至關重要的要素之一。不管是面試或者實際工作中,我們都不得不面對幾個問題:是幾個九?消息會不會丟失?如何保證冪等?如何順序消費?在這篇文章中,筆者會和大家一起去看 Kafka 是如何設計的。
2 可靠性分析
針對上面的幾個問題,Kafka 需要考慮包括并不限于以下問題:- 可用性
如何避免腦裂問題(這個要了解 Kafka 的 leader 選舉機制)
– 多副本機制,支持容災備份
- 數據一致性如何保證
- 數據同步要如何實現
- 消息問題
broker回復投遞成功,但是消息丟失了。出現這種情況,一般是以下幾種情況:
- acks 配置成 0,生產者以為自己投遞成功了,但其實并沒有成功寫入 leader
- 消息持久化在 leader 分區,但是沒有同步給 follower 就宕機了
– 消費者消費消息遇到消息丟失或者消息重復處理
- 消息丟失
- 消費者拿到消息了,但是處理過程中發生異常
- 消費者提交消費位移的設計不合理
- 重復消費
– 消息需要有序消費
我們知道 Kafka 是分區內消息有序的。當然,需要有序的消息就只能使用一個分區,無疑是以 Kafka 的水平擴展能力作為代價的。如果是需要全局有序,而我們又確定使用 Kafka,而且單分區的吞吐量不能滿足要求,那么我們只能自己進行額外設計來保證了。
2.1 acks配置對消息丟失的影響
2.1.1 acks=1
消息成功寫入 leader 后,就會告訴生產者投遞成功。如上圖例子,一共三個分區,其中 follower1 和 follower2 均屬于 ISR。假設 leader 成功寫入 3 和 4 之后,ISR 還沒同步,leader 就宕機了,這樣就丟失了 3 和 4,如下圖:
2.1.2 acks=-1 或者 acks=all
消息不僅要成功寫入 leader,還要 ISR 中的所有 follower 同步完成后,才會告知生產者投遞成功。還是 2.1.1 的例子,這里無非會有兩種情況:
- leader 在同步完成后宕機
- leader 在同步完成前宕機
這個配置對 Kafka 的性能會有較大影響,需要自己斟酌得失。
2.2 unclean.leader.election.enable
這個配置是用來控制 Kafka 是否可以選舉非 ISR 中的副本為 leader,0.11.0.0 之后的版本默認為 false。雖然設置為 true 可以提高 Kafka 的可用性,但是會降低 Kafka 數據的可靠性。2.3 小結
上面提出的問題均有指出和 Kafka 相關部分的設計是哪些,這里再總結一下:- 如何避免腦裂問題——了解 Kafka 的 leader 選舉機制
- 數據同步&數據一致性問題——了解 Kafka 的多副本設計
- 消息順序消費問題——了解 Kafka 的日志同步機制(分區有序)
3.副本設計
副本( Replica )是分布式系統中常見的概念之一,指的是分布式系統對數據和服務提供的一種冗余方式。我們知道 Kafka 通過多副本機制,增強了容災備份的能力,并且實現了故障轉移。這無疑是大大提高了 Kafka 的可用性,下面筆者會帶著大家一起看 Kafka 的副本機制是如何設計的。
在此之前,先簡單復習幾個相關的概念:
- 副本是相對分區而言的,即副本是指某個分區的副本
- 在多副本情況下,其中一個副本為 leader,其它均為 follower。只有 leader 對外提供服務,follower 僅同步leader 數據
- 分區中所有的副本集合稱為 AR,ISR 是與 leader 保持同步狀態的副本集合,leader 也是 ISR 中的一員
- LEO 是每個分區下一條消息要寫入的位置
- HW 是 ISR 中最小的 LEO,消費者只能拉取 HW 之前的消息
3.1 失效副本
正常情況下,分區中所有副本都應該屬于 ISR,但是網絡具有不可靠性。因此,難免在某一個時刻會有一些成員會被踢出 ISR,這些副本要么處于同步失效狀態,要么功能失效,這些副本統稱為失效副本。功能失效指的是無法工作,比如某個 broker 宕機了,那么在它上面的分區就會失效。
同步失效又是怎么判斷是否同步失效的呢?是通過參數 replica.lag.time.max.ms 來判斷的,默認是 10000 毫秒。當某個 follower 同步滯后 leader 的時間超過 10 秒,則判定為同步失效。
具體實現原理如下:
當 follower 將 leader LEO 之前的消息全部同步完成,那么會認為該 follower 已經追上 leader,并更新 lastCaughtUpTimeMs。Kafka 的副本管理器有一個副本過期檢測的定時任務,如果發現當前時間 - lastCaughtUpTimeMs > 10秒,則判定同步失效。
除了時間設置以外,還有另一個參數 replica.lag.max.message(默認4000,這個是 broker 級別的參數),也是用來判定失效副本的。
一般情況下,這兩個參數都是使用默認值就可以,因為如果沒有調優經驗,自己亂配置,容易導致 ISR 變動過于頻繁。同時,需要監控失效副本的數量,因為它是衡量 Kafka 是否健康的一個很重要的指標。
PS:新加入的副本因子/宕機恢復后重新加入的副本在追趕上 leader 之前,也會一直處于失效狀態。
3.1.1 失效副本的作用
失效副本為 Kafka 帶來了什么收益呢?為什么需要設計這么一個狀態呢?大家不妨試想下:假設允許 ISR 中有一個副本同步一直跟不上 leader。當 leader 發生宕機時,這個 follower 被選舉成了新的 leader,那么這時就會發生消息丟失。
一般會造成副本失效基本是以下兩個原因:
- follower 副本進程卡頓,在一段時間內無法發起同步請求,比如說頻繁發生 FULL GC
- follower 同步過慢,在一段時間內無法追上 leader,比如 I/O有問題(筆者實際工作中遇到過一次,公司搭建自己的物理機房,用了二手服務器,有一臺服務器I/O老化導致讀寫數據慢,導致了副本失效,消息堆積等問題)
3.2 LEO 與 HW
這一小節會更進一步去講解它們之間的關系,讓大家可以更清楚 Kafka 的副本同步機制。假設現在有 3 個 broker,某個 topic 有 1 個分區,3 個副本。現在有一個 producer 發送了一條消息,這 3 個副本會發生些什么操作。
具體步驟如下:
- producer 發送消息到 leader
- leader 將消息追加到日志,并且更新日志的偏移量
- follower 執行定時任務向 leader 發送 fetch request 同步數據,該請求會帶上自己的 LEO
- leader 讀取本地日志,并更新 follower 的信息
- leader 返回 fetch response 給 follower,response 會包含 HW
- follower 將消息追加到本地日志,并更新日志的偏移量
1.一個新建的 topic 被寫入了 5 條消息,兩個 follower 去拉取數據
2.leader 給 follower 返回 fetch response,并且 leader 又被寫入了 5 條消息
其中 follower1 同步了 2 條數據,而 follower2 同步了 3 條數據。
而 follower 的 HW = min(自己的LEO, 同步回來的HW)
3.follower 再次同步數據,同時 leader 又被寫入了 5 條消息
leader 更新了 HW
4.leader 給 follower 返回 fetch response
根據公式,follower 更新 HW = 3
在一個分區中,leader 所在 broker 會記錄所有副本的 LEO 和 自己的 HW;而 follower 所在的 broker 只會記錄自己的 LEO 和 HW。因此,在邏輯層面上,我們可以得到下圖:
0.11.0.0版本之前,Kafka 是基于 HW 的同步機制,但是這個設計有可能出現數據丟失和數據不一致的問題。Kafka 后面的版本通過 leader epoch 來進行優化。
3.3 數據丟失 & 數據不一致的解決方案
3.2小節說到了 LEO 與 HW 的更新機制,并且提到這種設計可能會出現數據丟失和數據不一致。我們先一起來看下這兩個問題是如何產生的。3.3.1 數據丟失
假設某一分區在某一時刻的狀態如下圖(L 代表是 leader):可以看見副本A的 LEO 是 2,HW 是 1;副本B的 LEO 是 2,HW 是 2。顯然,哪怕沒有新的消息寫入副本B中,副本A也要過一小段時間才能追上副本A,并更新 HW。
假設在副本A更新 HW = 2之前,A宕機了,隨后立馬就恢復。這里會有一個截斷機制——根據宕機之前持久化的HW 恢復消息。也就是說,A只恢復了 m1,m2 丟失了。
再假設 A 剛恢復,B 也宕機了,A 成為了 leader。這時 B 又恢復了,并成為了 follower。由于 follower 的 HW 不能比 leader 的 HW 高,所以 B 的 m2 也丟失了。
總結:這里大家可以發現 follower 的 HW 更新是有一定間隙的,像我這個例子其實 follower 是拿到 m2 了,只不過 HW 要追上 leader 需要等下一次的 fetch request。除非配置 acks=-1 并且配置min.insync.replicas 大于 1,unclean.leader.election.enable = true 才行。
3.3.2 數據不一致
假設某一分區在某一時刻,副本A 的 HW = 2,LEO = 2;副本B 的 HW = 1,LEO = 1。
又假設它們同時掛了,B 先恢復。這時,B 會成為 leader,如下圖:
此時,B 寫入新消息 m3,并將 HW、LEO 更新為 2。此時,A 也恢復了。由于 A 的 HW 也是 2,所以沒有截斷消息。如下圖:
這樣一來,A 中 offset = 1 的消息是 m2,B 中 offset = 1 的消息是 m3,數據不一致了。
3.3.3 leader epoch
為了解決 3.3.1 和 3.3.2 的問題,Kafka 從 0.11.0.0 開始引入 leader epoch,在需要截斷時使用 leader epoch 作為依據,而不再是 HW。如果看框架代碼比較多的同學應該知道 epoch 是相當于版本的這么一個概念。leader epoch 的初始值是 0,每變更一次 leader,leader epoch 就會增加 1。另外,每個副本中還會增加一個矢量<LeaderEpoch => StartOffset>,其中 StartOffset 是當前 leader epoch 下寫入第一條消息的偏移量。每個副本的 Log 下都有一個 leader-epoch-checkpoint 文件,在發生 leader 變更時,會將對應的矢量追加到這個文件中。
3.3.3.1 解決數據丟失問題
還是3.3.1的例子,只不過多了 leader epoch 矢量信息。
副本A:HW=1,LEO=2,LE(leader epoch)=0,Offset(StartOffset)=0
leader-副本B:HW=2,LEO=2,LE=0,Offset(StartOffset)=0
假設在副本A更新 HW = 2之前,A宕機了,隨后立馬就恢復。不過這里不會立馬進行截斷日志操作,而是會發送一個 OffsetsForLeaderEpochRequest 請求給 B,B 作為目前的 leader 在收到請求之后會返回 OffsetsForLeaderEpochResponse 給 A。
我們先來看下 OffsetsForLeaderEpochRequest 和 OffsetsForLeaderEpochResponse 的數據結構。如下圖:
- OffsetsForLeaderEpochRequest
A 會將自己的 leader epoch 信息給 leader(A的 leader epoch 這里簡化成 LE_A)。這里會出現兩種情況:
– 變更了 leader
B 會返回 LE_A+1 的 StartOffset 給 A
– 沒有變更 leader
B 會返回 A 的 LEO 給 A
因此,我們可以把 OffsetsForLeaderEpochRequest 看作是一個查詢 follower 當前 leader_epoch 的 LEO。
- OffsetsForLeaderEpochResponse
這個例子中,B 會返回2給 A,而此時的 A 的 LEO 剛好是 2,所以不用進行截斷日志。如下圖:
如果此時B掛了,A成了 leader,并有 m3 寫入,就會得到下圖:
可以看見 m2 并沒有丟失,并且也更新了 leader_epoch 矢量為 (1,2)。
3.3.3.2 解決數據不一致問題
上圖是3.3.2的例子。副本A是 leader,B 是 follower。
A 的 HW=2,LEO=2,LE=(0,0)
B 的 HW=1,LEO=1,LE=(0,0)
此時,A 和 B 同時宕機,并且 B 先恢復成為了 leader。此時,epoch 變成了 1。另外,新消息 m3 成功寫入,就會得到下圖:
接著,A 也恢復了,這時 A 不會急著截斷日志,而是給 leader 發送 OffsetsForLeaderEpochRequest,B 會返回 LEO = 1 給 A。因此,A 會截斷日志,刪除 m2。之后,再給 B 發送 fetch request,得到 B 的響應并更新后,將得到下圖:
這樣數據不一致問題就解決了。
這里大家可能會有疑問,m2不是丟失了嗎?是的,這種設計因為更新具有一定的間隙,并且沒有事務管理,所以會有丟失消息的風險。
從 CAP 定理來看,這里的設計屬于 AP。為什么這么說呢?大家不妨想一下,如果為了不丟失數據,這里加了事務控制的設計,那么對于分區而言它的吞吐量是會下降的,甚至是不可用的,因為響應速度是由短板的副本所決定的。對于定位是高吞吐量的 Kafka 而言,這顯然是不可接受的。
3.4 小結
Kafka 通過多副本機制增強了容災備份的能力,并且基于多副本機制實現了故障轉移,避免了單點問題,但同時也引進了新的問題——數據丟失和數據不一致。從 0.11.0.0 版本開始,Kafka 增加了 leader epoch,它對這兩個問題進行了優化。雖然無法完全避免消息丟失,但是從實際的使用角度而言,這個問題其實并不大。有實際工作經驗的同學應該都知道,我們發送消息難以避免需要重推,哪怕消息中間件做到了百分百不丟失,其實我們在使用時仍然會做防止消息丟失的設計。相對而言,數據一致性就更重要了,否則很容易讓訂閱消息的下游系統出現臟數據。4 leader 選舉機制
在 Kafka 集群中會有一個或者多個 broker,其中有一個 broker 會被選舉為控制器,它負責管理整個集群中所有分區和副本的狀態。分區的 leader 出現故障時,由控制器負責為其選舉新的 leader;當某個分區的 ISR 發生變化時,由控制器負責通知所有 broker 更新其元數據信息;當某個 topic 的分區數量發生變化時,還是由控制器負責分區的重新分配。因此,只要控制器正常工作,分區的 leader 就是唯一的,不會有腦裂問題。那么, Kafka 是如何保證控制器只有一個的呢?如果控制器發生異常了怎么辦?控制器的選舉和異常恢復又是怎樣的?
4.1 控制器
控制器是 broker 維度的角色,它負責管理整個集群中所有分區和副本的狀態。Kafka 中的控制器選舉工作依賴于 ZooKeeper,成功競選為控制器的 broker 會在 ZooKeeper 中創建 /controller 臨時節點,節點會存儲以下信息:
網友整理
注冊時間:
網站:5 個 小程序:0 個 文章:12 篇
-
51998
網站
- 12
小程序
-
1030137
文章
-
747
會員
熱門網站
- 各百科-專業百科問答知識名網站 m.geelcn.com
- 免費軟件,綠色軟件園,手機軟件下載,熱門游戲下載中心-中當網 m.deelcn.com
- 魔扣科技 www.ylptlb.cn
- 體育新聞_國際體育資訊_全球體育賽事-中名網 www.feelcn.com/tiyu/tiyuxinwen/
- 食品安全_健康飲食_舌尖上的安全-中名網 www.feelcn.com/shenghuo/shipinanquan/
- 中合網 www.heelcn.com
- 中當網 www.deelcn.com
- 魔扣網站維護代運營 www.ylptlb.cn/tg
- 中合網-健康養生知識科普名站 m.heelcn.com
- 各百科 www.geelcn.com
最新入駐小程序
熱門文章
- 民以食為天 離線人臉識別助力打造智慧食堂 08-20
- 青桔單車發布3款新車 已進入150個城市 08-13
- 民間大神用Win7毛玻璃UI風格改造Win10:情懷滿滿 08-06
- 網站標題是否可以修改?怎么改不影響網站權重? 11-19
- 關于網站標題和正文的匹配度分析 09-29
- 從滾石、華納到環球,三個關鍵詞讀懂網易云為何成版權方最愛 08-12
- 天眼被注冊為煙草商標,中國控煙協會要求嚴查 08-13
- 深圳實現5G獨立組網全覆蓋 已累計建設5G基站超4.6萬個 08-17
- 滴滴App內嵌買車服務 已在十余城上線 08-06
- 關鍵詞的密度要結合頁面版式來調整 11-28