Kafka為了保證數(shù)據(jù)不丟失, 對topic進(jìn)行了分區(qū)備份, 然后通過在ISR中選舉Leader來保證Fail-Over的實(shí)現(xiàn), 本節(jié)會稍微長一點(diǎn).
- 官網(wǎng)文檔的說明
- PacificA論文的說明
1. Kafka的Topic是如何做備份的
Kafka中一個(gè)邏輯上的Topic
實(shí)際上是分布在多個(gè)服務(wù)器上的Partition, 這些Parition中有一個(gè)是Leader Partition, 以及眾多跟隨Leader
的備份, 可以稱之為Follower來實(shí)現(xiàn)Fail-Over
其它的消息隊(duì)列系統(tǒng)一般都提供默認(rèn)的備份功能, 讓一個(gè)不活躍的slaver去跟隨主隊(duì)列的數(shù)據(jù), 整個(gè)設(shè)計(jì)非常的重. 在Kafka中沒有這樣的復(fù)制備份的概念, 所有的Topic都是有replication的, 只不過有一些topic的replication數(shù)是0
在Kafka的設(shè)計(jì)中Leader Partition
會直接接收來自Producer
端寫入的數(shù)據(jù), 而Follower Partition
會作為一個(gè)Consumer
去Leader這里讀取數(shù)據(jù), 并寫入到自己的本地的文件中. 這樣的設(shè)計(jì)下, 部分Follower
可能會落后Leader
, 有一個(gè)壞處是的時(shí)間窗口讓Leader
和Follower
們的數(shù)據(jù)不一致. 這種設(shè)計(jì)的一個(gè)好處是Follower
的拉取邏輯比較簡單, 而且是批量拉取的實(shí)現(xiàn)讓吞吐比較高.
一個(gè)ISR
是active的, 也就是可用, 需要滿足兩個(gè)條件:
- 它所在的node(broker)需要能在zookeeper里維護(hù)心跳
- 作為一個(gè)備份, 它和leader之間的數(shù)據(jù)差異不能太大(可配置的一個(gè)threhold)
滿足以上條件的Follower被稱之為 "in sync", 而不是"alive" or "failed". Leader會持續(xù)跟蹤這些"in sync""的Follower. 如果一個(gè)Follower不滿足以上兩點(diǎn)條件了, Leader就會把它從Follower的列表中剔除.
NOTE: replica.lag.time.max.ms 控制落后threahold的配置項(xiàng)
在傳統(tǒng)的分布式系統(tǒng)中, 會使用各種方法來解決Fail-Over問題, 俗稱拜占庭問題. Kafka在設(shè)計(jì)中并不能解決這個(gè)問題, Kafka不會假設(shè)一個(gè)Node會惡意發(fā)布虛假信息
拜占庭問題: https://en.wikipedia.org/wiki/Byzantine_fault_tolerance
在Kafka中, 一條信息被commit意味著它被存放在了這個(gè)Topic關(guān)聯(lián)的所有的"in sync replicas"中. 只有commit的消息可以被Consumer端拉取到. 這樣Consumer可以不用擔(dān)心它讀取的信息會丟失, 它和其它的Consumer讀取的內(nèi)容可能不一致.
Producers, 可以通過一個(gè)配置項(xiàng)來標(biāo)記自己是否等待commit狀態(tài)完成.
在Procuer的架構(gòu)中可以看到Response有三種不同的觸發(fā)條件
通過設(shè)置最小的ISR接受次數(shù), 可以控制Kafka返回Response到Producer的行為, 這個(gè)最小次數(shù)可以設(shè)置成0, 意味著數(shù)據(jù)僅僅被保存在Leader上就認(rèn)為結(jié)束了. 一般來說只要有一個(gè)ISR或者, 就認(rèn)為這個(gè)數(shù)據(jù)沒有丟失, 可以執(zhí)行Kafka自己的Fail-Over策略.
Kafka可以在一個(gè)Node失效后成功的fail-over, 但無法在網(wǎng)絡(luò)錯(cuò)誤(分區(qū)錯(cuò)誤)后保持可用性.
2. 基于日志的Replication實(shí)現(xiàn)的抉擇
基于Replicate Log(日志復(fù)制策略)的分布式系統(tǒng)備份的實(shí)現(xiàn), 是非常傳統(tǒng)的策略. 只要整個(gè)系統(tǒng)是基于state machine(狀態(tài)機(jī)) 實(shí)現(xiàn)的, 就可以使用這種策略來做HA, 大家都知道的就是MySQL的HA策略, 從Master上不斷的把Bin-Log復(fù)制到Standby上去, Standby基于Log執(zhí)行每一個(gè)在Master上已經(jīng)執(zhí)行過的操作來保證Standby的機(jī)器和Master的一致性
Note: State Machine: https://en.wikipedia.org/wiki/State_machine_replication 維基百科里還介紹了多個(gè)Standby時(shí)的選擇Paxos
Replicate Log模型要求所有的日志必須按照順序被發(fā)送到standby的節(jié)點(diǎn)上去, 最簡單的策略就是在Leader節(jié)點(diǎn)不死的情況下, 由Leader節(jié)點(diǎn)來整理需要發(fā)送的Log的順序.
在Kafka里實(shí)際上就是當(dāng)ISR去Leader拉數(shù)據(jù)的時(shí)候, Leader保證發(fā)送給所有的ISR的message都是同樣的順序即可.
當(dāng)Leader節(jié)點(diǎn)不行掛掉了, 我們就需要去從所有的Follower里面選舉一個(gè)出來做新的Leader. 在選舉過程中, Follower可能也會掛掉, 甚至Follower上的數(shù)據(jù)可能不全. 參考上一章介紹的ISR的兩個(gè)約束, 在Commit前需要的最小ISR數(shù)越多, 當(dāng)Leader掛掉時(shí)可以參與選舉的節(jié)點(diǎn)就越多, 但是吞吐率就越低. 用戶需要在可用性和吞吐之間做平衡
經(jīng)典策略是多數(shù)選擇. 當(dāng)我們維護(hù)2f+1個(gè)備份時(shí), 每次commit前強(qiáng)制必須至少有f+1個(gè)備份機(jī)接收到消息, 然后當(dāng)這此commit完成后leader掛掉的時(shí)候, 我們從所有的follower中選一個(gè)擁有消息數(shù)最多的點(diǎn)做leader. 這個(gè)策略保證了, 只要掛掉的follower不超過f個(gè), 那么至少應(yīng)該有一個(gè)點(diǎn), 擁有所有的消息. 這個(gè)就是個(gè)簡單的抽屜原理的實(shí)現(xiàn).
選舉算法的好處是: 延遲Latency被比較快的節(jié)點(diǎn)決定, 這樣沒有異構(gòu)集群下慢機(jī)器的問題.
壞處也很明顯, 為了實(shí)現(xiàn)3個(gè)穩(wěn)定備份需要7個(gè)Follower和7次網(wǎng)絡(luò)傳輸, 代價(jià)非常的高. 所以一般這種策略用來實(shí)現(xiàn)元數(shù)據(jù)的存儲, 像Zookpeer, HDFS NameNode的元數(shù)據(jù)備份等. 而數(shù)據(jù), 分布在DataNode上的HFile是無法使用這種策略的.
HDFS NameNode的備份策略: http://blog.cloudera.com/blog/2012/10/quorum-based-journaling-in-cdh4-1
Kafka 使用一個(gè)工程化版的Quorum. 像前文說的Kafka會維護(hù)一個(gè)in-sync replicas (ISR) 來對Leader進(jìn)行備份, 這些ISR的信息是被寫到Zookeeper中的. 只有足夠數(shù)量的ISR接收到message才算這條message被commit, 所以也就只有ISR可以參與選舉過程. Kafka相當(dāng)于使用一定的端到端延遲為代價(jià)換取只需要維護(hù)f+1個(gè)備份來保證數(shù)據(jù)的可用性. end-to-end latency 的trade off體現(xiàn)在可能某個(gè)follower就是慢, 導(dǎo)致commit比較慢, 進(jìn)而導(dǎo)致consumer就是讀不到這條信息
如果用戶覺得特別慢, 用戶可以直接通過配置項(xiàng)來動態(tài)決定commit的策略. Keep It Simple and Stupid. 認(rèn)為系統(tǒng)的使用者是充分理解自己業(yè)務(wù)狀態(tài)的, 這是Apache的幾個(gè)組件的共同特色
3. ISR全部掛掉時(shí)的抉擇
起碼有以下兩個(gè)選擇
- 等待某個(gè)ISR恢復(fù), 它可能只是掉線了, 過一會IT部的哥們說不定把它連上呢?
- 選舉一個(gè)沒有那么全消息的follower成為leader
unclean.leader.election.enable
為True時(shí)選擇第二個(gè)策略, 恢復(fù)回來的數(shù)據(jù)是unclean的, 為False時(shí)使用第一個(gè)策略, 等待開發(fā)人員恢復(fù)一個(gè)備份. 默認(rèn)是False
4. 可用性與持久性的抉擇
Procuder端在寫的時(shí)候可以選擇: 生產(chǎn)者被通知寫入成功, 是否這條message被拉取到 0個(gè), 至少1個(gè), 或者所有(all) 的 Follower之后才發(fā)生.
這里的通知"acknowledged"不代表這個(gè)數(shù)據(jù)就被持久化了. 當(dāng)Producer端設(shè)置acks=all, 當(dāng)所有的Follower拉取message后就會返回成功, 但它們拉取消息不代表它們成功寫入本地, 甚至拉取過程本身都因?yàn)榫W(wǎng)絡(luò)波動失敗掉.
這里生產(chǎn)者得知寫入成功, 和這條消息達(dá)到commit狀態(tài)之間也沒有必然關(guān)系. 比如一個(gè)生產(chǎn)者寫入一個(gè)Replication = 2
, ISR mini size = 1
的topic, 兩個(gè)follower嘗試從leader拉取數(shù)據(jù)時(shí), producer這里會異步得到一個(gè)成功的回執(zhí). 然而很可能其中一個(gè)在拉取過程中失敗了, 這個(gè)message的真實(shí)備份數(shù)只有1份!
-
unclean leader election
, 是等待一個(gè)全信息的ISR恢復(fù), 還是直接使用臟數(shù)據(jù)讓業(yè)務(wù)接續(xù), 并承受可能的數(shù)據(jù)損失. -
minimum ISR size
. 當(dāng)Producer端設(shè)置 ack=all時(shí)需要的最小ISR完成數(shù), 這個(gè)設(shè)置的越大, 延遲就越高, 吞吐也可能會降低. 設(shè)置的越小, 丟失數(shù)據(jù)的風(fēng)險(xiǎn)就越大
5. Replication的管理
決定什么時(shí)候進(jìn)恢復(fù)流程, 以及選舉算法在哪里跑也很重要. Kafka會從所有的Broker里面選一個(gè)Controller, 這個(gè)Controller會周期性檢測Broker級別的錯(cuò)誤, 通過zookeeper心跳. 當(dāng)發(fā)現(xiàn)某個(gè)Broker不可用時(shí), 就需要對它上面的Leader Partition執(zhí)行選舉算法過程來決定新的partition.
同樣的它也需要維護(hù)失敗節(jié)點(diǎn)上的ISR等配置信息, 這樣單點(diǎn)在大規(guī)模集群里運(yùn)行速度比較快, 過程比較容易理解. 而復(fù)雜的配置同步過程, 狀態(tài)和心跳維持過程剝離到Zookeeper去做, 整個(gè)套件里只需要維護(hù)Zab
這樣一個(gè)復(fù)雜的同步策略就OK了, Kafka自身不用去關(guān)注完全拜占庭問題.
6. PacificA - Kafka官方文檔的"靈感來源"
本節(jié)很多內(nèi)容和圖片來自知乎專欄 @分布式和存儲的那些事, 在原文基礎(chǔ)上說明了PacificA 的設(shè)計(jì)映射到Kafka上是什么
6.1 設(shè)計(jì)策略
這是微軟的分布式存儲框架, 是Kafka整個(gè)備份設(shè)計(jì)的靈感來源
- 設(shè)計(jì)了一個(gè)通用和抽象的復(fù)制框架,模型容易驗(yàn)證正確性寞焙,實(shí)現(xiàn)不同的策略實(shí)例. 對應(yīng)Kafka中的ISR復(fù)制策略
- 配置管理和數(shù)據(jù)復(fù)制分離映皆,用著名的Paxos負(fù)責(zé)管理配置和副本信息. 實(shí)際上在Kafka里是用Zookeeper來保證的, Zookeeper用的算法是Zab
- 將錯(cuò)誤檢測和配置更新容在數(shù)據(jù)復(fù)制的交互里面,去中心化帽氓,降低瓶頸
6.2 系統(tǒng)框架
存儲集群:對應(yīng)Kafka中的Broker, 實(shí)現(xiàn)數(shù)據(jù)的存儲, 同時(shí)數(shù)據(jù)會再存儲節(jié)點(diǎn)之間復(fù)制 . 大數(shù)據(jù)實(shí)際上是被切分成partition, 分布式的存在多個(gè)broker上的.
配置管理集群:對應(yīng)Kafka在Zookeeper里維護(hù)的配置信息, 包括ISR的狀態(tài), Leader的狀態(tài), Broker的狀態(tài)等.
6.3 數(shù)據(jù)復(fù)制策略
pacificA也是用Leader和Follower來實(shí)現(xiàn)一份數(shù)據(jù)存放在多地的, 如果Leader掛掉了, 就從Follower里選一個(gè)出來做新的Leader
- 更新記錄進(jìn)入主副本節(jié)點(diǎn)處理,為該記錄分配Sn(Serial Number)胞谈,然后將該記錄插入prepare list填具,該list上的記錄按照sn有序排列;
這里對應(yīng)前面提到的當(dāng)Follower去拉取Leader信息時(shí), Leader有責(zé)任保證寫過去的消息對所有的Follower的順序是一致的. - 主副本節(jié)點(diǎn)將攜帶sn的記錄發(fā)往從節(jié)點(diǎn)艰额,從節(jié)點(diǎn)同樣將該記錄插入到prepare list澄港;
這里對應(yīng)Follower們把數(shù)據(jù)刷到本地的segement里 - 一旦主節(jié)點(diǎn)收到所有從節(jié)點(diǎn)的響應(yīng),確定該記錄已經(jīng)被正確寫入所有的從節(jié)點(diǎn)柄沮,那就將commit list向前移動回梧,并將這些消息應(yīng)用到主節(jié)點(diǎn)的狀態(tài)機(jī);
主節(jié)點(diǎn)提交后即可給客戶端返回響應(yīng)祖搓,同時(shí)向所有從節(jié)點(diǎn)發(fā)送消息狱意,告訴從節(jié)點(diǎn)可以提交剛剛寫入的記錄了。
這里對應(yīng)全面提到的commit策略
這個(gè)設(shè)計(jì)和Kafka一樣, Consumer讀數(shù)據(jù)時(shí), 總是從Leader Partition讀, 不會從Follower上讀. 這是Kafka和HDFS的一個(gè)顯著差異, HDFS是允許從Replication的DataNode上讀數(shù)據(jù)的, Kafka不行
6.4 配置管理服務(wù)
前面的pacificA架構(gòu)中我們提到該框架一個(gè)很重要的組成部分是配置管理服務(wù)棕硫。該服務(wù)維護(hù)所有復(fù)制組信息. 也就是ISR的信息
在系統(tǒng)運(yùn)行過程中可能有以下幾種情形導(dǎo)致復(fù)制組信息發(fā)生變更:
- 復(fù)制組從節(jié)點(diǎn)離線髓涯;
- 復(fù)制組主節(jié)點(diǎn)離線袒啼;
- 復(fù)制組增加新節(jié)點(diǎn)
對每種情況處理方法:
- 從節(jié)點(diǎn)離線哈扮,主節(jié)點(diǎn)能檢測到該事件(檢測方法下面部分說明)纬纪,此時(shí)主節(jié)點(diǎn)向配置管理服務(wù)匯報(bào)最新的拓?fù)洌⒁獯藭r(shí)的拓?fù)渲幸呀?jīng)不包含離線的從節(jié)點(diǎn)滑肉;
- 主節(jié)點(diǎn)離線包各,從節(jié)點(diǎn)能檢測到該事件(檢測方法下面部分說明),此時(shí)從節(jié)點(diǎn)向配置管理服務(wù)匯報(bào)最新的拓?fù)浒忻恚⒁獯藭r(shí)拓?fù)渲幸呀?jīng)不包含離線的主節(jié)點(diǎn)问畅,同時(shí)從節(jié)點(diǎn)會將自己設(shè)置為主節(jié)點(diǎn)
- 復(fù)制組增加新節(jié)點(diǎn),可能是因?yàn)樵瓉黼x線的節(jié)點(diǎn)又重新上線了六荒,此時(shí)主節(jié)點(diǎn)向配置管理服務(wù)匯報(bào)最新的拓?fù)浠つ罚⒁獯藭r(shí)的拓?fù)渲屑由显撔略龉?jié)點(diǎn)
6.5 節(jié)點(diǎn)異常檢測
在Kafka里這是由Controller和Zookeeper一起來完成的, 在微軟的設(shè)計(jì)里不是,這是整篇論文看下來最大的差異
系統(tǒng)運(yùn)行過程中難免出現(xiàn)節(jié)點(diǎn)宕機(jī)離線等諸多異常。如何保證在節(jié)點(diǎn)異常情況下協(xié)議的正常運(yùn)行是分布式系統(tǒng)設(shè)計(jì)中的關(guān)鍵問題掏击。pacificA是一種強(qiáng)一致協(xié)議卵皂,通過主節(jié)點(diǎn)來維護(hù)多副本數(shù)據(jù)一致性。
pacificA使用了lease機(jī)制來保證不會產(chǎn)生腦裂問題砚亭。主節(jié)點(diǎn)與從節(jié)點(diǎn)維護(hù)心跳信息:Leader定期發(fā)送心跳灯变,只要Follower響應(yīng)該心跳,Leader就確定自己還是Leader捅膘。對于以下兩種可能的異常:
- 如果Leader在一定時(shí)間內(nèi)(lease period)未收到Follower對心跳的回應(yīng)添祸,它就需要向配置維護(hù)集群匯報(bào)這個(gè)信息, 并發(fā)起一輪Leader選舉. 這個(gè)過程在Kafka里是不存在的.
- 如果Follower在一定時(shí)間內(nèi)(grace period)未收到Leader的心跳信息,那么其認(rèn)為Leader異常寻仗,于是向配置管理服務(wù)匯報(bào)回信復(fù)制集拓?fù)淙忻冢瑢eader從復(fù)制集中移除,同時(shí)將自己提升為新的Leader愧沟。
只要lease period
<= grace peroid
蔬咬,就不會產(chǎn)生出現(xiàn)兩個(gè)Leader問題,因?yàn)椋?/p>
- 假如主從之間出現(xiàn)網(wǎng)絡(luò)分區(qū)沐寺。只要lease period <= grace peroid林艘,主必然先檢測到從沒有回復(fù)的事件,于是他開始停止作為主服務(wù)混坞,并且通知配置管理服務(wù)狐援;
- 由于lease period <= grace peroid,那么從節(jié)點(diǎn)檢測到主節(jié)點(diǎn)異常則是在主節(jié)點(diǎn)停止作為主以后究孕,此時(shí)該副本集是不存在主的啥酱,因此,必然不會產(chǎn)生二主現(xiàn)象厨诸。
6.6 節(jié)點(diǎn)異诚庖螅恢復(fù)
上面是Primary和Secondary-2兩個(gè)節(jié)點(diǎn)之間出現(xiàn)了網(wǎng)絡(luò)異常。接下來微酬,配置管理服務(wù)器可能會收到三個(gè)節(jié)點(diǎn)發(fā)起的拓?fù)渲嘏渲谜埱驧1绘趋,M2和M3颤陶。
M1:主節(jié)點(diǎn)DATA NODE 2將自己降級為從,同時(shí)向配置管理服務(wù)發(fā)起移除dn3的請求陷遮,我的理解是滓走,如果配置管理服務(wù)批準(zhǔn)了該請求,那么該節(jié)點(diǎn)重新成為主帽馋;
M2:從節(jié)點(diǎn)DATA NODE 3可能只是與主節(jié)點(diǎn)出現(xiàn)網(wǎng)絡(luò)分區(qū)搅方,進(jìn)程還在,此時(shí)從節(jié)點(diǎn)DATA NODE 3向配置管理服務(wù)發(fā)起Change Primary請求并將主節(jié)點(diǎn)DATA NODE 2從復(fù)制集中移除绽族;
M3:因?yàn)橹鞴?jié)點(diǎn)DATA NODE 2放棄了主節(jié)點(diǎn)的身份姨涡,因此,可能導(dǎo)致DATA NODE 1在心跳周期內(nèi)也無法獲得心跳吧慢,于是它也有可能發(fā)起一次Change Primary請求绣溜,將主節(jié)點(diǎn)dn2移除,將自己提升為新的主娄蔼。
而配置管理服務(wù)器會根據(jù)請求的先后順序來處理怖喻,對于上述M1、M2岁诉、M3請求锚沸,只有一個(gè)會得到批準(zhǔn),假如M1得到批準(zhǔn)涕癣,那么新的結(jié)構(gòu)就變?yōu)椋?/p>
此時(shí)DATA NODE 3便被從復(fù)制集中剔除了哗蜈。當(dāng)然,如果DATA NODE 3進(jìn)程依然存活的話坠韩,它接下來還是可以向配置管理服務(wù)申請將自己加入復(fù)制組的距潘。
在確定新的復(fù)制組拓?fù)浜螅€要經(jīng)歷一個(gè)reconciliation過程只搁。該過程的主要目的是保持從節(jié)點(diǎn)和主節(jié)點(diǎn)的數(shù)據(jù)一致性音比,主要過程是主節(jié)點(diǎn)將自己的prepare list上的操作記錄全部提交至狀態(tài)機(jī)并將自己的prepare list記錄同步至從。對于從節(jié)點(diǎn)來說氢惋,有可能其prepare list落后于主節(jié)點(diǎn)洞翩,這時(shí)候需要補(bǔ)齊,有可能其prepare list領(lǐng)先于從節(jié)點(diǎn)焰望,這時(shí)候需要截?cái)嗌б凇o論如何,由于滿足prepare list和commit list的定式關(guān)系熊赖,因此不會出現(xiàn)已經(jīng)提交的記錄被回滾的情況来屠。