項(xiàng)目經(jīng)驗(yàn)分享|SOFAStack 社區(qū) 袁梓為:開(kāi)源項(xiàng)目從不是遙不可及

開(kāi)源之夏個(gè)人專(zhuān)訪與項(xiàng)目經(jīng)驗(yàn)分享持續(xù)開(kāi)放中,歡迎已從開(kāi)源之夏畢業(yè)或正在參與開(kāi)源之夏活動(dòng)的學(xué)生、導(dǎo)師一同加入專(zhuān)訪行動(dòng),掃描文末二維碼填寫(xiě)專(zhuān)訪問(wèn)卷,與大家分享你眼中的開(kāi)源之夏!
本期項(xiàng)目經(jīng)驗(yàn)分享來(lái)自 SOFAStack?社區(qū)中選學(xué)生——袁梓為,在開(kāi)源之夏 2023 中承擔(dān)的項(xiàng)目是?結(jié)合 NWR 實(shí)現(xiàn) Flexible raft,用于自定義 Quorum 的大小。

#?關(guān)于 SOFAStack 社區(qū)
SOFAStack?(Scalable Open Financial Architecture Stack)是一套用于快速構(gòu)建金融級(jí)云原生架構(gòu)的中間件,也是在金融場(chǎng)景里錘煉出來(lái)的最佳實(shí)踐,并且具備以下特點(diǎn):* 開(kāi)放:技術(shù)棧全面開(kāi)源共建、 保持社區(qū)中立、兼容社區(qū) 兼容開(kāi)源生態(tài),組件可插拔, SOFAStack 組件與其它開(kāi)源組件可相互集成或替換* 金融級(jí):包含構(gòu)建金融級(jí)云原生架構(gòu)所需的各個(gè)組件,讓用戶更加專(zhuān)注于業(yè)務(wù)開(kāi)發(fā),滿足用戶場(chǎng)景的現(xiàn)狀和未來(lái)需求,經(jīng)歷過(guò)大規(guī)模場(chǎng)景的錘煉,特別是嚴(yán)苛的金融場(chǎng)景* 云原生:基于 SOFAStack 可快速搭建云原生微服務(wù)體系,快速開(kāi)發(fā)更具可靠性和擴(kuò)展性、更加易于維護(hù)的云原生應(yīng)用
官網(wǎng):https://www.sofastack.tech/
# 項(xiàng)目基本信息
項(xiàng)目名稱:結(jié)合 NWR 實(shí)現(xiàn) Flexible raft,用于自定義 Quorum 的大小
項(xiàng)目導(dǎo)師:劉源遠(yuǎn)
項(xiàng)目描述:SOFA JRaft 是一個(gè)基于 RAFT 一致性算法的生產(chǎn)級(jí)高性能 Java 實(shí)現(xiàn),它運(yùn)行過(guò)程分為兩個(gè)階段,即 Leader 選舉和日志復(fù)制。
在原始的 RAFT 算法中,Leader 選舉和日志復(fù)制都需要獲得多數(shù)派成員的支持。而 NWR 模型則可以在動(dòng)態(tài)調(diào)整一致性強(qiáng)度的場(chǎng)景中使用,它需要滿足 W+R>N,以保證強(qiáng)一致性。JRaft 將 RAFT 和 NWR 結(jié)合起來(lái),使得用戶可以根據(jù)不同的業(yè)務(wù)需求來(lái)動(dòng)態(tài)調(diào)整 Quorum 的數(shù)量。
例如,在一個(gè)寫(xiě)多讀少的場(chǎng)景中,用戶可以將多數(shù)派的數(shù)量從 3 調(diào)整為 2,以降低達(dá)成共識(shí)的條件,從而提高寫(xiě)請(qǐng)求的效率。同時(shí),為了保證 RAFT 的正確性,寫(xiě) Quorum 的調(diào)整需要付出代價(jià),即讀 Quorum 的數(shù)量也需要相應(yīng)調(diào)整。JRaft 支持成員變更,因此用戶可以配置 (0,10] 范圍內(nèi)的數(shù)來(lái)計(jì)算 W 和 R 的具體值(需要滿足 W+R=10)。通過(guò)使用 Flexible JRaft,用戶可以根據(jù)自己的業(yè)務(wù)需求來(lái)靈活地調(diào)整一致性強(qiáng)度,使得分布式系統(tǒng)在不同場(chǎng)景下都可以獲得最佳的性能和正確性。
項(xiàng)目鏈接:https://summer-ospp.ac.cn/org/prodetail/2395a0390
# 項(xiàng)目實(shí)現(xiàn)思路
為了更清晰的展示 NWR 是如何與 JRaft 結(jié)合的,我們這里避免繁瑣的代碼細(xì)節(jié)展示,試著用簡(jiǎn)單的圖片來(lái)體現(xiàn)他們之間的聯(lián)系。
我們知道,NWR 是一種在分布式存儲(chǔ)系統(tǒng)中用于控制一致性級(jí)別的策略。N 在分布式存儲(chǔ)系統(tǒng)中,代表有多少份備份數(shù)據(jù) 。W 代表一次成功的更新操作要求至少有 W 份數(shù)據(jù)寫(xiě)入成功。R 代表一次成功的讀數(shù)據(jù)操作要求至少有 R 份數(shù)據(jù)成功讀取。
例如,在一個(gè) N=3 的 Raft 集群中,W 是 2、R 是 2 的時(shí)候,W+R>N,這種情況對(duì)于集群而言就是強(qiáng)一致性的。

而當(dāng)?R+W<=N?時(shí),無(wú)法保證數(shù)據(jù)的強(qiáng)一致性。因?yàn)槌晒?xiě)和成功讀集合可能不存在交集,這樣讀操作無(wú)法讀取到最新的更新數(shù)值,也就無(wú)法保證數(shù)據(jù)的強(qiáng)一致性。例如下圖中 W=2,R=1 的時(shí)候,W+R=3=N 滿足不了強(qiáng)一致性。

在分布式一致性算法 Raft 中,Configuration(配置)是用于管理集群成員和集群狀態(tài)的一組信息。每個(gè)服務(wù)器節(jié)點(diǎn)都有一個(gè)配置,用于確定當(dāng)前集群的成員身份和狀態(tài)。
所以,我們能否將 Quorum 交給 Configuration 來(lái)管理呢?隨著 Configuration 的集群成員信息變化,ReadQuorum 與 WriteQuorum 也隨之變化,這樣我們對(duì)于 Quorum 的管理也更加靈活方便。在 Configuration 中也持有當(dāng)前 raft 集群的模式標(biāo)志,用來(lái)表示當(dāng)前集群是 majority 模式還是 flexible 模式。另外,除了節(jié)點(diǎn)配置信息的持久化,我們同樣也要對(duì) Quorum 進(jìn)行持久化。所以,Quorum 放到 Configuration 之后,將隨著集群配置信息一同進(jìn)行持久化。

在設(shè)計(jì)好 Quorum 之后,接下來(lái)需要考慮哪些地方需要對(duì) Quorum NWR 模型進(jìn)行適配。在 Raft 算法中,會(huì)牽涉到選票的模塊有:選舉、日志復(fù)制、一致性讀與成員變更。所以,對(duì)于這些模塊,我們都需要兼容以往的多數(shù)派投票邏輯(Majority 模式),并且在這基礎(chǔ)上去適配新的 Flexible 模式。

接下來(lái)我們簡(jiǎn)單闡述以上四個(gè)模塊對(duì)于 NWR 模型的適配規(guī)則:
Leader 選舉模塊:?一個(gè)節(jié)點(diǎn)想要成為 leader,會(huì)經(jīng)過(guò)以下幾個(gè)階段:預(yù)投票、正式投票、當(dāng)選 leader。所以對(duì)于 preVote、electSelf、becomeLeader 等等與多數(shù)派模型相關(guān)的方法都會(huì)涉及代碼的變更。
日志復(fù)制模塊:?當(dāng) leader 收到客戶端的事務(wù)請(qǐng)求或者 follower 與 leader 數(shù)據(jù)存在差距時(shí),會(huì)調(diào)用 Replicator#sendEntries 去復(fù)制日志,日志復(fù)制消息屬于事務(wù)消息;而心跳消息和探測(cè)消息,則是由 Replicator#sendEmptyEntries 發(fā)送的。
在日志復(fù)制中,部分代碼會(huì)使用到 BallotBox#appendPendingTask 方法來(lái)構(gòu)造一個(gè)待投票的 Ballot 并放置到投票箱中,所有復(fù)制過(guò)程中參與投票的方法都需要對(duì) NWR 進(jìn)行適配。
一致性讀模塊:?對(duì)于一致性讀模塊,在 Raft 共識(shí)算法中,讀取 R 個(gè)節(jié)點(diǎn)其實(shí)體現(xiàn)在 R 個(gè)節(jié)點(diǎn)的心跳響應(yīng)。通過(guò) R 個(gè)節(jié)點(diǎn)的心跳,能保證這個(gè)節(jié)點(diǎn)一定是當(dāng)前的 leader,所以一定擁有最新的數(shù)據(jù),因此我們只需要保證 R 個(gè)心跳的相應(yīng)即可。
對(duì)應(yīng)的修改部分,像 NodeImpl#ReadIndexHeartbeatResponseClosure 這樣的方法,我們可以看到執(zhí)行了心跳消息的多數(shù)派確認(rèn)模型的邏輯,ReadIndexHeartbeatResponseClosure 構(gòu)造器里面?zhèn)魅肓?quorum 的值,這里我們同樣需要進(jìn)行修改。
成員變更模塊:對(duì)于 JRaft 成員變更來(lái)講,核心邏輯是采用單成員變更的方式,即使需要同時(shí)變更多個(gè)成員時(shí),也是會(huì)先整理出新 add 與新 remove 的成員,再逐個(gè)進(jìn)行單成員變更。其核心方法 addPeer、removePeer、changePeers、resetPeers 等等都會(huì)涉及 NWR 模型的適配。
我們的代碼基于以上的思路進(jìn)行設(shè)計(jì),有很多的細(xì)節(jié)處理在實(shí)現(xiàn)過(guò)程中并不容易做好。在最后還要書(shū)寫(xiě)測(cè)試用例,跑通所有測(cè)試,進(jìn)行 Jepsen 一致性檢驗(yàn)等等工作。
# 開(kāi)源之夏個(gè)人隨訪
--參與開(kāi)源之夏和開(kāi)源--
OSPP:請(qǐng)簡(jiǎn)單介紹一下自己,并分享一下自己的開(kāi)源經(jīng)歷吧
袁梓為:大家好,我叫袁梓為,是廣東工業(yè)大學(xué)計(jì)算機(jī)科學(xué)與技術(shù)專(zhuān)業(yè)大三的一名學(xué)生。之前有過(guò)一段 Apache RocketMQ 社區(qū)的開(kāi)源經(jīng)歷,主要做的是 RocketMQ-5.0 客戶端 SDK 與 Spring 的集成[1];過(guò)去也有在 SOFAJRaft 項(xiàng)目中有代碼貢獻(xiàn)。
OSPP:這是你第一次參與開(kāi)源之夏,為什么選擇參加這個(gè)活動(dòng)?
袁梓為:參加開(kāi)源之夏既是興趣,亦是挑戰(zhàn)。作為學(xué)生,能夠接觸到開(kāi)源社區(qū),無(wú)疑能夠磨練我們的技術(shù),也能夠接觸到最新的行業(yè)動(dòng)態(tài)。開(kāi)源之夏為學(xué)生提供了一個(gè)學(xué)習(xí)、實(shí)踐和參與開(kāi)源社區(qū)的寶貴機(jī)會(huì)。這對(duì)于技術(shù)成長(zhǎng)、職業(yè)發(fā)展以及與開(kāi)源社區(qū)建立聯(lián)系都具有重要意義。
OSPP:為什么會(huì)選擇申請(qǐng) SOFAStack 社區(qū)的項(xiàng)目?有為申請(qǐng)項(xiàng)目而提前做什么溝通和準(zhǔn)備么?
袁梓為:選擇申請(qǐng) SOFAStack-SOFAJRaft 社區(qū)項(xiàng)目的初衷,其實(shí)是希望能夠深入地學(xué)習(xí) Raft 算法。當(dāng)時(shí)正好有做 MIT6.824 的想法,不過(guò)考慮到自己主語(yǔ)言是 JAVA,剛好 SOFA-JRaft 又是基于 JAVA 實(shí)現(xiàn)的生產(chǎn)級(jí)高性能 RAFT 算法。所以看到項(xiàng)目要求是結(jié)合 Quorum NWR 模型實(shí)現(xiàn)更加靈活的 RAFT 算法時(shí),我就產(chǎn)生了濃厚的興趣。
其實(shí)在大一的時(shí)候我有關(guān)注過(guò) SOFA-JRaft 社區(qū),不過(guò)當(dāng)時(shí)能力遠(yuǎn)遠(yuǎn)還沒(méi)有達(dá)到能夠貢獻(xiàn)代碼的水平,所以只是每天逛逛社區(qū),嘗試著去理解源碼。在大二上學(xué)期閱讀 JRaft 源碼的時(shí)候,偶然注意到 JRaft 所使用的 Netty 時(shí)間輪算法部分涉及循環(huán)的代碼可以優(yōu)化,于是提交了我開(kāi)源世界里的第一個(gè) ISSUE:The problem of Low Efficiency and Multiple Cycles In the TimeWheel Algorithm[2],雖然只是很小的一個(gè) PR,但也算作我邁向開(kāi)源社區(qū)的第一步吧。
在有了一次代碼合并經(jīng)歷之后,我也大致懂得了在社區(qū)做開(kāi)源的流程。之后也會(huì)時(shí)常關(guān)注社區(qū)的動(dòng)態(tài),于是在后面又嘗試著解決了社區(qū)里的另一個(gè)小需求:Auto Commit Mode for Applying Log Iterator[3]。從剛開(kāi)始接觸一個(gè)陌生的社區(qū),到最后參加 JRaft 的開(kāi)源之夏活動(dòng),會(huì)發(fā)現(xiàn)其實(shí)參與開(kāi)源項(xiàng)目也并不是想象中的那么遙不可及。所以當(dāng)我有了做 SOFAJRaft 選題的想法后,就立刻與源遠(yuǎn)導(dǎo)師溝通,源遠(yuǎn)哥也十分積極的回應(yīng),與他交流我對(duì)選題的思考。
OSPP:在開(kāi)發(fā)過(guò)程中有遇到什么困難或挑戰(zhàn)么?你是如何克服困難解決這些問(wèn)題的?
袁梓為:開(kāi)發(fā)中確實(shí)有遇到一些困難和挑戰(zhàn)。
比如當(dāng)初在對(duì) Quorum 類(lèi)進(jìn)行設(shè)計(jì)時(shí),遇到不少坑,如果不考慮好他的構(gòu)造設(shè)計(jì)與動(dòng)態(tài)變更的情況,程序運(yùn)行過(guò)程中很容易出錯(cuò)。剛開(kāi)始我是基于?個(gè)抽象類(lèi),去設(shè)計(jì)兩個(gè) Quorum ?類(lèi),分別代表 MajorityQuorum 和 FlexibleQuorum,即是多數(shù)派模型和 NWR 模型的區(qū)別。但是后來(lái)發(fā)現(xiàn)這兩個(gè) Quorum 的區(qū)別只是 W 和 R 的計(jì)算規(guī)則不同,其實(shí)我們完全可以??個(gè)??對(duì)這兩個(gè) Quorum 的讀寫(xiě)數(shù)值進(jìn)?不同的計(jì)算。在經(jīng)過(guò)?系列的測(cè)試與調(diào)整后,最終我們選擇了只使??個(gè) Quorum 進(jìn)?處理,這個(gè) Quourm 內(nèi)部包含有 W 和 R 兩個(gè)屬性,他們的計(jì)算規(guī)則依靠 BallotFactory 來(lái)進(jìn)?,最后我們只需要把 Quorum 和 factor 整合到 Configuration ??即可。
除了這個(gè)問(wèn)題以外,由于要對(duì) Flexible Raft 進(jìn)行線性一致性驗(yàn)證,SOFA-JRaft 需要用到 Jepsen 框架進(jìn)行校驗(yàn)。然而這個(gè)框架是用 clojure 語(yǔ)言書(shū)寫(xiě),之前從沒(méi)有接觸過(guò)這門(mén)語(yǔ)言,所以我也是花了一段時(shí)間去學(xué)習(xí) clojure 語(yǔ)法、理解 Jepsen 框架。在大概理解了 JRaft-Jepsen 的校驗(yàn)原理后,還需要自己搭建一個(gè)測(cè)試環(huán)境。因?yàn)椴](méi)有現(xiàn)成的測(cè)試平臺(tái)可以使用,于是我又用 Docker 模擬 Ubuntu 環(huán)境下的多節(jié)點(diǎn)去進(jìn)行驗(yàn)證。這個(gè)過(guò)程中,也遇到了很多麻煩:比如 Ubuntu 容器之間如何進(jìn)行 SSH 免密登錄通信、容器內(nèi)依賴工具缺少導(dǎo)致的一系列問(wèn)題。這些問(wèn)題要是沒(méi)處理好,都會(huì)影響最后的檢驗(yàn)效果。當(dāng)然這些比較棘手的問(wèn)題,在向源遠(yuǎn)導(dǎo)師請(qǐng)教、反復(fù)查閱資料、自己不斷試錯(cuò)之后,還是得到了解決。
OSPP:導(dǎo)師和社區(qū)有為你帶來(lái)哪些幫助?
袁梓為:在整個(gè)開(kāi)發(fā)過(guò)程中,有遇到任何難點(diǎn)與困惑點(diǎn),源遠(yuǎn)導(dǎo)師都會(huì)特別耐心地解答。在某些代碼設(shè)計(jì)上有不妥的地方,源遠(yuǎn)哥也會(huì)指導(dǎo)我如何更優(yōu)雅地去實(shí)現(xiàn)它。社區(qū)的其他導(dǎo)師也會(huì)積極回應(yīng)我的留言,在整個(gè)開(kāi)發(fā)過(guò)程里,提供了非常大的幫助,也讓我學(xué)到了很多有用的東西。
--參與開(kāi)源社區(qū)--
OSPP:在你眼中,SOFAStack 是一個(gè)怎樣的社區(qū)?
袁梓為:SOFAStack 社區(qū)是一個(gè)致力于推動(dòng)云原生技術(shù)發(fā)展的開(kāi)源社區(qū)。它通過(guò)開(kāi)源合作、技術(shù)創(chuàng)新和社區(qū)生態(tài)系統(tǒng)建設(shè),為開(kāi)發(fā)者提供了豐富的資源和平臺(tái),促進(jìn)了云原生應(yīng)用的普及和應(yīng)用。
OSPP:你在開(kāi)源社區(qū)中是否有超出預(yù)期的收獲?
袁梓為:我在參與 JRaft 項(xiàng)目的開(kāi)發(fā)中,深入學(xué)習(xí)和實(shí)踐了 Raft 算法。通過(guò)閱讀源碼、與導(dǎo)師溝通請(qǐng)教,解決了不少實(shí)戰(zhàn)開(kāi)發(fā)中遇到的問(wèn)題。這段經(jīng)歷將是編程道路上寶貴的財(cái)富。
--寄語(yǔ)--
OSPP:參與開(kāi)源之夏對(duì)你的專(zhuān)業(yè)技能提升和未來(lái)發(fā)展規(guī)劃有帶來(lái)哪些幫助么?
袁梓為:通過(guò)參與開(kāi)源之夏,我得到了經(jīng)驗(yàn)豐富的導(dǎo)師的指導(dǎo)和支持,他們提供寶貴的反饋和指導(dǎo),幫助我解決問(wèn)題、優(yōu)化代碼,并培養(yǎng)良好的軟件開(kāi)發(fā)實(shí)踐。我還積累到了實(shí)踐項(xiàng)目經(jīng)驗(yàn),并在開(kāi)源社區(qū)中做出有意義的貢獻(xiàn)。這些實(shí)踐和貢獻(xiàn)對(duì)于我們的職業(yè)發(fā)展具有積極影響,還能夠增加就業(yè)的競(jìng)爭(zhēng)力。
OSPP:有什么話想對(duì)計(jì)劃參加開(kāi)源之夏活動(dòng)的學(xué)弟學(xué)妹們說(shuō)?
袁梓為:我們可以充分利用這段時(shí)間,深入學(xué)習(xí)所選項(xiàng)目的技術(shù)棧和開(kāi)發(fā)流程,并積極參與實(shí)踐。通過(guò)解決問(wèn)題、編寫(xiě)代碼和與導(dǎo)師、社區(qū)成員交流,獲得寶貴的經(jīng)驗(yàn)和技能提升。
END
專(zhuān)欄編輯:大夢(mèng)
校對(duì):校大山、袁梓為
制圖:GoodWhite
[1]?https://github.com/apache/rocketmq-spring/pull/554
[2]?https://github.com/sofastack/sofa-jraft/issues/926
[3]?https://github.com/sofastack/sofa-jraft/issues/957

專(zhuān)欄投稿請(qǐng)聯(lián)系開(kāi)源小助手:kaiyuanzhixia 或?qū)诰庉嫞篐ungryfish34(備注“專(zhuān)欄投稿”加速通過(guò)),或填寫(xiě)下方專(zhuān)訪信息收集問(wèn)卷。
