1 背景
在分布式系統(tǒng)應(yīng)用中,高可用、一致性是經(jīng)常面臨的問(wèn)題,針對(duì)不同的應(yīng)用場(chǎng)景,我們會(huì)選擇不同的架構(gòu)方式,比如master-slave、基于ZooKeeper選主。隨著時(shí)間的推移,出現(xiàn)了基于Raft算法自動(dòng)選主的方式,Raft是在Paxos的基礎(chǔ)上,做了一些簡(jiǎn)化和限制,比如增加了日志必須是連續(xù)的,只支持領(lǐng)導(dǎo)者、跟隨者和候選人三種狀態(tài),在理解和算法實(shí)現(xiàn)上都相對(duì)容易許多。
1)DLedger 是openMessaging發(fā)布的一個(gè)基于 Raft 實(shí)現(xiàn)的JAVA類(lèi)庫(kù),可以方便引用到系統(tǒng)中,滿(mǎn)足其高可用、高可靠、強(qiáng)一致的需求,其中在RocketMQ中作為消息Broker存儲(chǔ)高可用實(shí)現(xiàn)的一種解決方案。
2)Raft將系統(tǒng)中的角色分為領(lǐng)導(dǎo)者(Leader)、跟從者(Follower)和候選人(Candidate):
- Leader:接受客戶(hù)端請(qǐng)求,定時(shí)發(fā)送心跳包,并向Follower同步請(qǐng)求日志,當(dāng)日志同步到大多數(shù)節(jié)點(diǎn)上后告訴Follower提交日志。
- Follower:接受并持久化Leader同步的日志,在Leader告之日志可以提交之后,提交日志。
- Candidate:Leader選舉過(guò)程中的臨時(shí)角色,該狀態(tài)下的節(jié)點(diǎn)會(huì)發(fā)起投票,嘗試選擇自己為主節(jié)點(diǎn),選舉成功后,不會(huì)存在該狀態(tài)下的節(jié)點(diǎn)
2 DLedger架構(gòu)設(shè)計(jì)
DLedger 的實(shí)現(xiàn)大體可以分為以下兩個(gè)部分:
- 選舉 Leader
- 日志復(fù)制
- 其整體架構(gòu)如下圖
注:圖引用官網(wǎng)
從上面的架構(gòu)圖中,有兩個(gè)核心類(lèi):DLedgerLeaderElector 和 DLedgerStore,選舉和文件存儲(chǔ)。選出 leader 后,再由 leader 去接收數(shù)據(jù)的寫(xiě)入,同時(shí)同步到其他的 follower,這樣就完成了整個(gè) Raft 的寫(xiě)入過(guò)程
3 DLedger選主源碼分析
3.1 下載源碼
從gitGub下載代碼(https://github.com/openmessaging/dledger ),idea引入后,我們發(fā)現(xiàn)整個(gè)代碼量很小,在分析代碼時(shí)比較容易.
3.2 選主流程分析
3.2.1 原理
raft的選主過(guò)程實(shí)際是一個(gè)狀態(tài)機(jī)的流轉(zhuǎn),在集群?jiǎn)?dòng)時(shí)每個(gè)節(jié)點(diǎn)的等待超時(shí)時(shí)間時(shí)隨機(jī)的,在第一個(gè)節(jié)點(diǎn)超時(shí)時(shí)間到來(lái),則主動(dòng)向其他節(jié)點(diǎn)發(fā)起投票,在收到半數(shù)以上的投票后晉升為leader(投票過(guò)程是個(gè)循環(huán)的過(guò)程),同時(shí)發(fā)送心跳請(qǐng)求,其他候選節(jié)點(diǎn)收到主節(jié)點(diǎn)的請(qǐng)求后,改變自己為follower節(jié)點(diǎn)。
- term: 任期,每一輪投票都是一個(gè)任期,默認(rèn)從0開(kāi)始
- Quorum機(jī)制:簡(jiǎn)單說(shuō)就是少說(shuō)半數(shù)以上,比如3個(gè)節(jié)點(diǎn),2個(gè)同意即可
- 超時(shí)時(shí)間: 在選舉時(shí),每個(gè)節(jié)點(diǎn)的超時(shí)時(shí)間在一定范圍內(nèi)是隨機(jī)的,這樣可以保證能夠順利選舉
3.2.2 代碼分析
整個(gè)狀態(tài)機(jī)的驅(qū)動(dòng),由線(xiàn)程每個(gè)10ms反復(fù)執(zhí)行DLedgerLeaderElector.maintainState()方法。下面重點(diǎn)來(lái)分析其狀態(tài)的驅(qū)動(dòng):
進(jìn)入到核心方法maintainAsCandidate() :
1.step1 初始化
- term : 投票輪次。
- ledgerEndTermLeader: 節(jié)點(diǎn)當(dāng)前的投票輪次。
- ledgerEndIndex: 當(dāng)前日志的最大序列,即下一條日志的開(kāi)始 index
- nextTimeToRequestVote: 下次發(fā)起投票的時(shí)間(隨機(jī)的)
- needIncreaseTermImmediately:是否立即投票,在后面中會(huì)說(shuō)明
在DLedger中每個(gè)節(jié)點(diǎn)的初始狀態(tài)WAIT_TO_REVOTE,所以第一輪只是做了初始化。其中只有 memberState.nextTerm()這個(gè)代碼會(huì)更改投票輪次
2.step2 投票
進(jìn)入到核心方法handleVote(),這個(gè)方法主要是判斷其他節(jié)點(diǎn)請(qǐng)求來(lái)后,根據(jù)自己的term和請(qǐng)求者的等判斷是否投贊成票
- ledgerEndIndex因?yàn)樵谌罩緩?fù)制過(guò)程中,每個(gè)節(jié)點(diǎn)的進(jìn)度有可能是不一樣的,所以在新的一輪選舉時(shí),這時(shí)不能投贊成票的
- 被選舉者 term 小于 選舉者的term,返回拒絕
- 被選舉者 term 大于 選舉者的term,則選舉者進(jìn)行下面操作:
- 變成candidate(或者保持candidate)
- 把needIncreaseImmediately設(shè)置為true。
- 返回 REJECT_TERM_NOT_READY,這個(gè)在后面提到。
這里補(bǔ)充說(shuō)明:
選舉者 的下一次狀態(tài)循環(huán)會(huì)進(jìn)入到maintainAsCandidate()函數(shù),然后因?yàn)閚eedIncreaseImmediately為true,所以把term更新,同時(shí)重置計(jì)時(shí)器。 但是并沒(méi)有立刻發(fā)出投票(此時(shí)選舉者 的CurrVoteFor還是null,使得接下來(lái)給之前的voting candidate 投贊成票可能)
獲取所有node投票結(jié)果后開(kāi)始計(jì)算票數(shù):
3.step3 仲裁
在收到所有節(jié)點(diǎn)的投票結(jié)果計(jì)數(shù)后,進(jìn)行仲裁,這里主要說(shuō)明下圖中這個(gè)條件
- acceptNum:同意的數(shù)量
- notReadyTermNum:未準(zhǔn)備好的數(shù)量(即結(jié)果為REJECT_TERM_NOT_READY)
這里沒(méi)有重置nextTimeToRequestVote的時(shí)間,即刻再發(fā)起一次投票。結(jié)合上面的說(shuō)明,這樣保證了被選者能盡快去拿到這些notRead的節(jié)點(diǎn)的贊成票。
最終經(jīng)過(guò)多次投票后,當(dāng)一個(gè)node節(jié)點(diǎn)獲取到半數(shù)以上投票后,更新自己未leader角色,同時(shí)向其他node節(jié)點(diǎn)發(fā)送heartBeat,其他節(jié)點(diǎn)在收到心跳信息后,將自己從candidate 變?yōu)閒ollower。
3.3 單元測(cè)試驗(yàn)證
3.3.1 編寫(xiě)單元測(cè)試
3.3.2 日志分析
3.4 應(yīng)用場(chǎng)景
- DLedger 作為 RocketMQ ( version>=4.5.0)的消息存儲(chǔ)已經(jīng)發(fā)布
- 基于DLedger 實(shí)現(xiàn)多節(jié)點(diǎn)的緩存同步更新
- 基于日志復(fù)制的副本容錯(cuò)處理
4 總結(jié)
- 這里只簡(jiǎn)單分析了選主過(guò)程,在閱讀源碼過(guò)程中會(huì)涉及很多java的基礎(chǔ)及netty的使用,比如AQS、CompletableFuture等,有助于提高我們的編碼能力。
- DLedger在初始化時(shí)是將節(jié)點(diǎn)角色設(shè)置為candidate而不是follower 這個(gè)和原Raft是不同的地方,在節(jié)點(diǎn)角色轉(zhuǎn)換過(guò)程中也稍有差別。
參考文獻(xiàn)
- https://github.com/openmessaging/dledger/wiki
- https://www.usenix.org/system/files/conference/atc14/atc14-paper-ongaro.pdf
作者:京東物流 郭慶海文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-760532.html
來(lái)源:京東云開(kāi)發(fā)者社區(qū) 自猿其說(shuō)Tech 轉(zhuǎn)載請(qǐng)注明來(lái)源文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-760532.html
到了這里,關(guān)于基于Raft算法的DLedger-Library分析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!