這篇文章主要介紹“SOFAJRaft-RheaKV MULTI-RAFT-GROUP實現(xiàn)分析SOFAJRaft的實現(xiàn)原理”,在日常操作中,相信很多人在SOFAJRaft-RheaKV MULTI-RAFT-GROUP實現(xiàn)分析SOFAJRaft的實現(xiàn)原理問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”SOFAJRaft-RheaKV MULTI-RAFT-GROUP實現(xiàn)分析SOFAJRaft的實現(xiàn)原理”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
創(chuàng)新互聯(lián)公司是一家專業(yè)提供尉犁企業(yè)網(wǎng)站建設,專注與網(wǎng)站制作、成都網(wǎng)站建設、H5網(wǎng)站設計、小程序制作等業(yè)務。10年已為尉犁眾多企業(yè)、政府機構等服務。創(chuàng)新互聯(lián)專業(yè)網(wǎng)絡公司優(yōu)惠進行中。
SOFAStackScalable Open Financial Architecture Stack 是螞蟻金服自主研發(fā)的金融級分布式架構,包含了構建金融級云原生架構所需的各個組件,是在金融場景里錘煉出來的最佳實踐。
RheaKV 是首個以 JRaft 為基礎實現(xiàn)的一個原生支持分布式的嵌入式鍵值(key、value)數(shù)據(jù)庫,現(xiàn)在本文將從 RheaKV 是如何利用 MULTI-RAFT-GROUP 的方式實現(xiàn) RheaKV 的高性能及容量的可擴展性的,從而進行全面的源碼、實例剖析。
通過對 Raft 協(xié)議的描述我們知道:用戶在對一組 Raft 系統(tǒng)進行更新操作時必須先經(jīng)過 Leader,再由 Leader 同步給大多數(shù) Follower。而在實際運用中,一組 Raft 的 Leader 往往存在單點的流量瓶頸,流量高便無法承載,同時每個節(jié)點都是全量數(shù)據(jù),所以會受到節(jié)點的存儲限制而導致容量瓶頸,無法擴展。
MULTI-RAFT-GROUP 正是通過把整個數(shù)據(jù)從橫向做切分,分為多個 Region 來解決磁盤瓶頸,然后每個 Region 都對應有獨立的 Leader 和一個或多個 Follower 的 Raft 組進行橫向擴展,此時系統(tǒng)便有多個寫入的節(jié)點,從而分擔寫入壓力,圖如下:
cdn.nlark.com/yuque/0/2019/jpeg/325890/1557569369003-7d4762a0-2590-48bc-afc9-b4e53b520054.jpeg">
此時磁盤及 I/O 瓶頸解決了,那多個 Raft Group 是如何協(xié)作的呢,我們接著往下看。
RheaKV 主要由 3 個角色組成:PlacementDriver(以下成為 PD) 、Store、Region。由于 RheaKV 支持多組 Raft,所以比單組場景多出一個 PD 角色,用來調(diào)度以及收集每個 Store 及 Region 的基礎信息。
PD 負責整個集群的管理調(diào)度、Region ID 生成等。此組件非必須的,如果不使用 PD,設置 PlacementDriverOptions 的 fake 屬性為 true 即可。PD 一般通過 Region 的心跳返回信息進行對 Region 調(diào)度,Region 處理完后,PD 則會在下一個心跳返回中收到 Region 的變更信息來更新路由及狀態(tài)表。
通常一個 Node 負責一個 Store,Store 可以被看作是 Region 的容器,里面存儲著多個分片數(shù)據(jù)。Store 會向 PD 主動上報 StoreHeartbeatRequest 心跳,心跳交由 PD 的 handleStoreHeartbeat 處理,里面包含該 Store 的基本信息,比如,包含多少 Region,有哪些 Region 的 Leader 在該 Store 等。
Region 是數(shù)據(jù)存儲、搬遷的最小單元,對應的是 Store 里某個實際的數(shù)據(jù)區(qū)間。每個 Region 會有多個副本,每個副本存儲在不同的 Store,一起組成一個Raft Group。Region 中的 Leader 會向 PD 主動上報 RegionHeartbeatRequest 心跳,交由 PD 的 handleRegionHeartbeat 處理,而 PD 是通過 Region 的 Epoch 感知 Region 是否有變化。
Muti-Raft-Group 的多 Region 是通過 RegionRouteTable 路由表組件進行管理的,可通過 addOrUpdateRegion、removeRegion 進行添加、更新、移除 Region,也包括 Region 的拆分。目前暫時還未實現(xiàn) Region 的聚合,后面會考慮實現(xiàn)。
“讓每組 Raft 負責一部分數(shù)據(jù)?!?/p>
數(shù)據(jù)分區(qū)或者分片算法通常就是 Range 和 Hash,RheaKV 是通過 Range 進行數(shù)據(jù)分片的,分成一個個 Raft Group,也稱為 Region。這里為何要設計成 Range 呢?原因是 Range 切分是按照對 Key 進行字節(jié)排序后再做每段每段切分,像類似 scan 等操作對相近 key 的查詢會盡可能集中在某個 Region,這個是 Hash 無法支持的,就算遇到單個 Region 的拆分也會更好處理一些,只用修改部分元數(shù)據(jù),不會涉及到大范圍的數(shù)據(jù)挪動。
當然 Range 也會有一個問題那就是,可能會存在某個 Region 被頻繁操作成為熱點 Region。不過也有一些優(yōu)化方案,比如 PD 調(diào)度熱點 Region 到更空閑的機器上,或者提供 Follower 分擔讀的壓力等。
Region 和 RegionEpoch 結構如下:
class Region { long id; // region id // Region key range [startKey, endKey) byte[] startKey; // inclusive byte[] endKey; // exclusive RegionEpoch regionEpoch; // region term List<Peer> peers; // all peers in the region } class RegionEpoch { // Conf change version, auto increment when add or remove peer long confVer; // Region version, auto increment when split or merge long version; } class Peer { long id; long storeId; Endpoint endpoint; }
Region.id:為 Region 的唯一標識,通過 PD 全局唯一分配。
Region.startKey、Region.endKey:這個表示的是 Region 的 key 的區(qū)間范圍 [startKey, endKey),特別值得注意的是針對最開始 Region 的 startKey,和最后 Region 的 endKey 都為空。
Region.regionEpoch:當 Region 添加和刪除 Peer,或者 split 等,此時 regionEpoch 就會發(fā)生變化,其中 confVer 會在配置修改后遞增,version 則是每次有 split 、merge(還未實現(xiàn))等操作時遞增。
Region.peers:peers 則指的是當前 Region 所包含的節(jié)點信息,Peer.id 也是由 PD 全局分配的,Peer.storeId 代表的是 Peer 當前所處的 Store。
由于數(shù)據(jù)被拆分到不同 Region 上,所以在進行多 key 的讀、寫、更新操作時需要操作多個 Region,這時操作前我們需要得到具體的 Region,然后再單獨對不同 Region 進行操作。我們以在多 Region上 scan 操作為例, 目標是返回某個 key 區(qū)間的所有數(shù)據(jù):
我們首先看 scan 方法的核心調(diào)用方法 internalScan 的異步實現(xiàn):
例如:com.alipay.sofa.jraft.rhea.client.DefaultRheaKVStore#scan(byte[], byte[], boolean, boolean)
我們很容易看到,在調(diào)用 scan 首先讓 PD Client 通過 RegionRouteTable.findRegionsByKeyRange 檢索 startKey、endKey 所覆蓋的 Region,最后返回的可能為多個 Region,具體 Region 覆蓋檢索方法如下:
檢索相關變量定義如下:
我們可以看到整個 RheaKV 的 range 路由表是通過 TreeMap 的進行存儲的,正呼應我們前面講過所有的 key 是通過對應字節(jié)進行排序存儲。對應的 Value 為該 Region 的 RegionId,隨后我們通過 Region 路由 regionTable 查出即可。
現(xiàn)在我們得到 scan 覆蓋到的所有 Region:List<Region>
在循環(huán)查詢中我們看到有一個“retryCause -> {}”的 Lambda 表達式很容易看出這里是加持異常重試處理,后面我們會講到,接下來會通過 internalRegionScan 查詢每個 Region 的結果。具體源碼如下:
這里也同樣有一個重試處理,可以看到代碼中根據(jù)當前是否為 Region 節(jié)點來決定是本機查詢還是通過RPC進行查詢,如果是本機則調(diào)用 rawKVStore.scan() 進行本地直接查詢,反之通過 rheaKVRpcService 進行 RPC 遠程節(jié)點查詢。最后每個 Region 查詢都返回為一個 future,通過 FutureHelper.joinList 工具類 CompletableFuture.allOf 異步并發(fā)返回結果 List<KVEntry>
。
我們再看看寫入具體流程。相比 scan 讀,put 寫相對比較簡單,只需要針對 key 計算出對應 Region 再進行存儲即可,我們可以看一個異步 put 的例子。
例如:com.alipay.sofa.jraft.rhea.client.DefaultRheaKVStore#put(java.lang.String, byte[])
我們可以發(fā)現(xiàn) put 基礎方法是支持 batch 的,即可成批提交。如未使用 batch 即直接提交,具體邏輯如下:
通過 pdClinet 查詢對應存儲的 Region,并且通過 regionId 拿到 RegionEngine,再通過對應存儲引擎 KVStore 進行 put,整個過程同樣支持重試機制。我們再回過去看看 batch 的實現(xiàn),很容易發(fā)現(xiàn)利用到了 Disruptor 的 RingBuffer 環(huán)形緩沖區(qū),無鎖隊列為性能提供了保障,代碼現(xiàn)場如下:
什么時候 Region 會拆分?
前面我們有講過,PD 會在 Region 的 heartBeat 里面對 Region 進行調(diào)度,當某個 Region 里的 keys 數(shù)量超過預設閥值,我們即可對該 Region 進行拆分,Store 的狀態(tài)機 KVStoreStateMachine 即收到拆分消息進行拆分處理。具體拆分源碼如下:
KVStoreStateMachine.doSplit 源碼如下:
StoreEngine.doSplit 源碼如下:
我們可以輕易的看到從原始 parentRegion 切分成 region 和 pRegion,并重設了 startKey、endKey 和版本號,并添加到 RegionEngineTable 注冊到 RegionKVService,同時調(diào)用 pdClient.getRegionRouteTable().splitRegion() 方法進行更新存儲在 PD 的 Region 路由表。
什么時候需要對 Region 進行合并?
既然數(shù)據(jù)過多需要進行拆分,那 Region 進行合并那就肯定是 2 個或者多個連續(xù)的 Region 數(shù)據(jù)量明顯小于絕大多數(shù) Region 容量則我們可以對其進行合并。這一塊后面會考慮實現(xiàn)。
通過上面我們知道,一個 Store 即為一個節(jié)點,里面包含著一個或者多個 RegionEngine,一個 StoreEngine 通常通過 PlacementDriverClient 對 PD 進行調(diào)用,同時擁有 StoreEngineOptions 配置項,里面配置著存儲引擎和節(jié)點相關配置。
我們以默認的 DefaultRheaKVStore 加載 StoreEngine 為例,DefaultRheaKVStore 實現(xiàn)了 RheaKVStore 接口的基礎功能,從最開始 init 方法,根據(jù) RheaKVStoreOptions 加載了 pdClinet 實例,隨后加載 storeEngine。
在 StoreEngine 啟動的時候,首先會去加載對應的 StoreEngineOptions 配置,構建對應的 Store 配置,并且生成一致性讀的線程池 readIndexExecutor、快照線程池 snapshotExecutor、RPC 的線程池 cliRpcExecutor、Raft 的 RPC 線程池 raftRpcExecutor,以及存儲 RPC 線程池 kvRpcExecutor、心跳發(fā)送器 HeartbeatSender 等,如果打開代碼,我們還能看到 metricsReportPeriod,打開配置可以進行性能指標監(jiān)控。
在 DefaultRheaKVStore 加載完所有工序之后,便可使用 get、set、scan 等操作,還包含對應同步、異步操作。
在這個過程中里面的 StoreEngine 會記錄著 regionKVServiceTable、regionEngineTable,它們分別掌握著具體每個不同的 Region 存儲的操作功能,對應的 key 即為 RegionId。
每個在 Store 里的 Region 副本中,RegionEngine 則是一個執(zhí)行單元。它里面記錄著關聯(lián)著的 StoreEngine 信息以及對應的 Region 信息。由于它也是一個選舉節(jié)點,所以也包含著對應狀態(tài)機 KVStoreStateMachine,以及對應的 RaftGroupService,并啟動里面的 RpcServer 進行選舉同步。
這個里面有個transferLeadershipTo方法,這個可被調(diào)用用于平衡當前節(jié)點分區(qū)的Leader,避免壓力重疊。
DefaultRegionKVService 是 RegionKVService 的默認實現(xiàn)類,主要處理對 Region 的具體操作。
需要特別講到的是,在具體的 RheaKV 操作時,F(xiàn)ailoverClosure 擔任著比較重要的角色,也給整個系統(tǒng)增加了一定的容錯性。假如在一次 scan 操作中,如果跨 Store 需要多節(jié)點 scan 數(shù)據(jù)的時候,任何網(wǎng)絡抖動都會造成數(shù)據(jù)不完整或者失敗情況,所以允許一定次數(shù)的重試有利于提高系統(tǒng)的可用性,但是重試次數(shù)不宜過高,如果出現(xiàn)網(wǎng)絡堵塞,多次 timeout 級別失敗會給系統(tǒng)帶來額外的壓力。這里只需要在 DefaultRheaKVStore 中,進行配置 failoverRetries 設置次數(shù)即可。
PlacementDriverClient 接口主要由 AbstractPlacementDriverClient 實現(xiàn),然后 FakePlacementDriverClient、RemotePlacementDriverClient 為主要功能。FakePlacementDriverClient 是當系統(tǒng)不需要 PD 的時候進行 PD 對象的模擬,這里主要講到 RemotePlacementDriverClient。
RemotePlacementDriverClient 通過PlacementDriverOptions 進行加載,并根據(jù)基礎配置刷新路由表;
RemotePlacementDriverClient 承擔著對路由表RegionRouteTable 的管控,例如獲取Store、路由、Leader節(jié)點信息等;
RemotePlacementDriverClient 還包含著 CliService,通過 CliService 外部可對復制節(jié)點進行操作運維,如addReplica、removeReplica、transferLeader。
由于很多傳統(tǒng)存儲中間件并不原生支持分布式,所以一直少有體感,Raft 協(xié)議是一套比較比較好理解的共識協(xié)議,SOFAJRaft 通俗易懂是一個非常好的代碼和工程范例,同時 RheaKV 也是一套非常輕量化支持多存儲結構可分片的嵌入式數(shù)據(jù)庫。寫一篇代碼分析文章也是一個學習和進步的過程,由此我們也可以窺探到了一些數(shù)據(jù)庫的基礎實現(xiàn),祝愿社區(qū)能在 SOFAJRaft / RheaKV 基礎上構建更加靈活和自治理的系統(tǒng)和應用。
到此,關于“SOFAJRaft-RheaKV MULTI-RAFT-GROUP實現(xiàn)分析SOFAJRaft的實現(xiàn)原理”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
本文名稱:SOFAJRaft-RheaKVMULTI-RAFT-GROUP實現(xiàn)分析SOFAJRaft的實現(xiàn)原理
網(wǎng)頁地址:http://jinyejixie.com/article36/gdjhsg.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、關鍵詞優(yōu)化、網(wǎng)站維護、網(wǎng)站內(nèi)鏈、虛擬主機、定制網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)