您好,登錄后才能下訂單哦!
本篇文章為大家展示了MetaServer 之什么是SOFARegistry,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
MetaServer 作為 SOFARegistry 的元數據中心,其核心功能可以概括為集群成員管理。分布式系統中,如何知道集群中有哪些節點列表,如何處理集群擴所容,如何處理集群節點異常,都是不得不考慮的問題。MetaServer 的存在就是解決這些問題,其在 SOFARegistry 中位置如圖所示: cdn.nlark.com/yuque/0/2019/png/338467/1568254454389-0cefa85d-131a-4c2d-a844-f66e2c9807b4.png">
MetaServer 通過 SOFAJRaft 保證高可用和一致性,類似于注冊中心,管理著集群內部的成員列表:
節點列表的注冊與存儲
節點列表的變更通知
節點健康監測
內部架構如下圖所示:
MetaServer 基于 Bolt, 通過 TCP 私有協議的形式對外提供服務,包括 DataServer, SessionServer 等,處理節點的注冊,續約和列表查詢等請求。
同時也基于 Http 協議提供控制接口,比如可以控制 session 節點是否開啟變更通知, 健康檢查接口等。
成員列表數據存儲在 Repository 中,Repository 被一致性協議層進行包裝,作為 SOFAJRaft 的狀態機實現,所有對 Repository 的操作都會同步到其他節點, 通過Rgistry來操作存儲層。
MetaServer 使用 Raft 協議保證數據一致性, 同時也會保持與注冊的節點的心跳,對于心跳超時沒有續約的節點進行驅逐,來保證數據的有效性。
在可用性方面,只要未超過半數節點掛掉,集群都可以正常對外提供服務, 半數以上掛掉,Raft 協議無法選主和日志復制,因此無法保證注冊的成員數據的一致性和有效性。整個集群不可用 不會影響 Data 和 Session 節點的正常功能,只是無法感知節點列表變化。
MetaServer 在啟動時,會啟動三個 Bolt Server,并且注冊 Processor Handler,處理對應的請求, 如下圖所示:
DataServer:處理 DataNode 相關的請求;
SessionServer:處理 SessionNode 相關的請求;
MetaServer:處理MetaNode相關的請求;
然后啟動 HttpServer, 用于處理 Admin 請求,提供推送開關,集群數據查詢等 Http 接口。
最后啟動 Raft 服務, 每個節點同時作為 RaftClient 和 RaftServer, 用于集群間的變更和數據同步。
各個 Server 的默認端口分別為:
meta.server.sessionServerPort=9610 meta.server.dataServerPort=9611 meta.server.metaServerPort=9612 meta.server.raftServerPort=9614 meta.server.httpServerPort=9615
由上節可知,DataServer 和 SessionServer 都有處理節點注冊請求的 Handler。注冊行為由 Registry 完成。注冊接口實現為:
@Override public NodeChangeResult register(Node node) { StoreService storeService = ServiceFactory.getStoreService(node.getNodeType()); return storeService.addNode(node); }
Regitsry 根據不同的節點類型,獲取對應的StoreService
,比如DataNode
,其實現為 DataStoreService
然后由 StoreService
存儲到 Repository
中,具體實現為:
// 存儲節點信息 dataRepositoryService.put(ipAddress, new RenewDecorate(dataNode, RenewDecorate.DEFAULT_DURATION_SECS)); //... // 存儲變更事件 dataConfirmStatusService.putConfirmNode(dataNode, DataOperator.ADD);
調用 RepositoryService#put
接口存儲后,同時會存儲一個變更事件到隊列中,主要用于數據推送,消費處理。
節點數據的存儲,其本質上是存儲在內存的哈希表中,其存儲結構為:
// RepositoryService 底層存儲 Map<String/*dataCenter*/, NodeRepository> registry; // NodeRepository 底層存儲 Map<String/*ipAddress*/, RenewDecorate<T>> nodeMap;
將RenewDecorate
存儲到該 Map 中,整個節點注冊的流程就完成了,至于如何和 Raft 協議進行結合和數據同步,下文介紹。
節點移除的邏輯類似,將節點信息從該 Map 中刪除,也會存儲一個變更事件到隊列。
不知道有沒有注意到,節點注冊的時候,節點信息被 RenewDecorate
包裝起來了,這個就是實現注冊信息續約和驅逐的關鍵:
private T renewal; // 節點對象封裝 private long beginTimestamp; // 注冊事件 private volatile long lastUpdateTimestamp; // 續約時間 private long duration; // 超時時間
該對象為注冊節點信息,附加了注冊時間、上次續約時間、過期時間。那么續約操作就是修改lastUpdateTimestamp
,是否過期就是判斷System.currentTimeMillis() - lastUpdateTimestamp > duration
是否成立,成立則認為節點超時進行驅逐。
和注冊一樣,續約請求的處理 Handler 為ReNewNodesRequestHandler
,最終交由 StoreService 進行續約操作。另外一點,續約的時候如果沒有查詢到注冊節點,會觸發節點注冊的操作。
驅出的操作是由定時任務完成,MetaServer 在啟動時會啟動多個定時任務,詳見ExecutorManager#startScheduler
,,其中一個任務會調用Registry#evict
,其實現為遍歷存儲的 Map, 獲得過期的列表,調用StoreService#removeNodes
方法,將他們從 Repository
中移除,這個操作也會觸發變更通知。該任務默認每3秒執行一次。
上文有介紹到,在處理節點注冊請求后,也會存儲一個節點變更事件,即:
dataConfirmStatusService.putConfirmNode(dataNode, DataOperator.ADD);
DataConfirmStatusService
也是一個由 Raft 協議進行同步的存儲,其存儲結構為:
BlockingQueue<NodeOperator> expectNodesOrders = new LinkedBlockingQueue(); ConcurrentHashMap<DataNode/*node*/, Map<String/*ipAddress*/, DataNode>> expectNodes = new ConcurrentHashMap<>();
expectNodesOrders
用來存儲節點變更事件;
expectNodes
用來存儲變更事件需要確認的節點,也就是說 NodeOperator
只有得到了其他節點的確認,才會從 expectNodesOrders
移除;
那么事件存儲到 BlockingQueue 里,哪里去消費呢? 看源碼發現,并不是想象中的使用一個線程阻塞的讀。
在ExecutorManager
中會啟動一個定時任務,輪詢該隊列有沒有數據。即周期性的調用Registry#pushNodeListChange
方法,獲取隊列的頭節點并消費。Data 和 Session 各對應一個任務。具體流程如下圖所示:
首先獲取隊列(expectNodesOrders)頭節點,如果為Null直接返回;
獲取當前數據中心的節點列表,并存儲到確認表(expectNodes);
提交節點變更推送任務(firePushXxListTask);
處理任務,即調用 XxNodeService 的 pushXxxNode 方法,即通過 ConnectionHandler 獲取所有的節點連接,發送節點列表;
收到回復后,如果需要確認,則會調用StroeService#confirmNodeStatus
方法,將該節點從expectNodes中移除;
待所有的節點從 expectNodes 中移除,則將此次操作從 expectNodesOrders 移除,處理完畢;
Data,Meta,Session Server 都提供 getNodesRequestHandler
,用于處理查詢當前節點列表的請求,其本質上從底層存儲 Repository 讀取數據返回,這里不在贅述。返回的結果的具體結構見 NodeChangeResult
類,包含各個數據中心的節點列表以及版本號。
后端 Repository 可以看作SOFAJRaft 的狀態機,任何對 Map 的操作都會在集群內部,交由 Raft 協議進行同步,從而達到集群內部的一致。從源碼上看,所有的操作都是直接調用的 RepositoryService
等接口,那么是如何和 Raft 服務結合起來的呢?
看源碼會發現,凡是引用 RepositoryService
的地方,都加了 @RaftReference
, RepositoryService
的具體實現類都加了 @RaftService
注解。其關鍵就在這里,其處理類為 RaftAnnotationBeanPostProcessor
。具體流程如下:
在 processRaftReference
方法中,凡是加了 @RaftReference
注解的屬性,都會被動態代理類替換,其代理實現見 ProxyHandler
類,即將方法調用,封裝為 ProcessRequest
,通過 RaftClient 發送給 RaftServer。
而被加了 @RaftService
的類會被添加到 Processor
類 中,通過 serviceId
(interfaceName + uniqueId) 進行區分。RaftServer 收到請求后,會把它生效到 SOFAJRaft 的狀態機,具體實現類為 ServiceStateMachine
,即會調用 Processor
方法,通過 serviceId 找到這個實現類,執行對應的方法調用。
當然如果本機就是主節點, 對于一些查詢請求不需要走Raft協議而直接調用本地實現方法。
這個過程其實和 RPC 調用非常類似,在引用方發起的方法調用,并不會真正的執行方法,而是封裝成請求發送到 Raft 服務,由 Raft 狀態機進行真正的方法調用,比如把節點信息存儲到 Map 中。所有節點之間的數據一致由Raft協議進行保證。
在分布式系統中,集群成員管理是避不開的問題,有些集群直接把列表信息寫到配置文件或者配置中心,也有的集群選擇使用 zookeeper 或者 etcd 等維護集群元數據,SOFARegistry 選擇基于一致性協議 Raft,開發獨立的MetaServer,來實現集群列表維護和變更實時推送,以提高集群管理的靈活性和集群的健壯性。
上述內容就是MetaServer 之什么是SOFARegistry,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。