SOFARegistry 是螞蟻金服開源的一個生產級、高時效、高可用的服務註冊中心。本系列將帶領你們一塊兒分析其MetaServer的實現機制,本文爲第一篇,介紹MetaServer整體架構。html
本系列整體參考了官方的博客,具體請參見"0xFF 參考"。你們能夠把參考做爲總綱,我這系列文章做爲註釋補遺翻閱。java
在微服務架構下,一個互聯網應用的服務端背後每每存在大量服務間的相互調用。例如服務 A 在鏈路上依賴於服務 B,那麼在業務發生時,服務 A 須要知道服務 B 的地址,才能完成服務調用。而分佈式架構下,每一個服務每每都是集羣部署的,集羣中的機器也是常常變化的,因此服務 B 的地址不是固定不變的。若是要保證業務的可靠性,服務調用者則須要感知被調用服務的地址變化。node
既然成千上萬的服務調用者都要感知這樣的變化,那這種感知能力便下沉成爲微服務中一種固定的架構模式:服務註冊中心。linux
服務註冊中內心,有服務提供者和服務消費者兩種重要的角色,服務調用方是消費者,服務被調方是提供者。對於同一臺機器,每每兼具二者角色,既被其它服務調用,也調用其它服務。服務提供者將自身提供的服務信息發佈到服務註冊中心,服務消費者經過訂閱的方式感知所依賴服務的信息是否發生變化。web
服務註冊中心在服務調用的場景中,扮演一個「中介」的角色,服務發佈者 (Publisher) 將服務發佈到服務註冊中心,服務調用方 (Subscriber) 經過訪問服務註冊中心就可以獲取到服務信息,進而實現調用。算法
Subscriber 在第一次調用服務時,會經過 Registry 找到相應的服務的 IP 地址列表,經過負載均衡算法從 IP 列表中取一個服務提供者的服務器調用服務。同時 Subscriber 會將 Publisher 的服務列表數據緩存到本地,供後續使用。當 Subscriber 後續再調用 Publisher 時,優先使用緩存的地址列表,不須要再去請求Registry。spring
+----------+ +---------+ |Subscriber| <--------+ +--------+ |Publisher| +----------+ | | +---------+ | | +----------+ | v |Subscriber| <--------+ +----------++ +---------+ +----------+ <---+ Registry | <-------+Publisher| | +----------++ +---------+ +----------+ | ^ |Subscriber| <--------+ | +----------+ | | +---------+ +----------+Publisher| +---------+
服務註冊中心Registry的最主要能力是服務註冊和服務發現兩個過程。數據庫
SOFARegistry 做爲服務註冊中心,分爲4個層,分別爲:編程
Client 層是應用服務器集羣。Client 層是應用層,每一個應用系統經過依賴註冊中心相關的客戶端 jar 包,經過編程方式來使用服務註冊中心的服務發佈和服務訂閱能力。bootstrap
Session層是服務器集羣。顧名思義,Session 層是會話層,經過長鏈接和 Client 層的應用服務器保持通信,負責接收 Client 的服務發佈和服務訂閱請求。
在服務註冊中心的服務端由於每一個存儲節點對應的客戶端的連接數據量有限,必須進行特殊的一層劃分用於專門收斂無限擴充的客戶端鏈接,而後在透傳相應的請求到存儲層。
該層只在內存中保存各個服務的發佈訂閱關係,對於具體的服務信息,只在 Client 層和 Data 層之間透傳轉發。Session 層是一個無數據狀態的代理層,能夠隨着 Client 層應用規模的增加而擴容。
由於 SOFARegistry 的服務發現須要較高的時效性,對外表現爲主動推送變動到客戶端,因此推送的主體實現也集中在 Session 層,內部的推拉結合主要是經過 Data 存儲層的數據版本變動推送到全部 Session 節點,各個 Session 節點根據存儲的訂閱關係和首次訂閱獲取的數據版本信息進行比對,最終肯定推送給那些服務消費方客戶端。
數據服務器集羣。Data 層經過分片存儲的方式保存着所用應用的服務註冊數據。數據按照 dataInfoId(每一份服務數據的惟一標識)進行一致性 Hash 分片,多副本備份,保證數據的高可用。Data 層能夠隨着數據規模的增加,在不影響業務的前提下實現平滑的擴縮容。
元數據服務器集羣。這個集羣管轄的範圍是 Session 服務器集羣和 Data 服務器集羣的服務器信息,其角色就至關於 SOFARegistry 架構內部的服務註冊中心,只不過 SOFARegistry 做爲服務註冊中心是服務於廣大應用服務層,而 Meta 集羣是服務於 SOFARegistry 內部的 Session 集羣和 Data 集羣,Meta 層可以感知到 Session 節點和 Data 節點的變化,並通知集羣的其它節點。
SOFARegistry 內部爲何要進行數據分層,是由於系統容量的限制。
在 SOFARegistry 的應用場景中,體量龐大的數據主要有兩類:Session 數據、服務信息數據。兩類數據的相同之處在於其數據量都會不斷擴展,而不一樣的是其擴展的緣由並不相同:
因此 SOFARegistry 經過分層設計,將兩種數據隔離,從而使兩者的擴容互不影響。
這也是 SOFARegistry 設計三層模型的緣由,經過 SessionServer 來負責與 Client 的鏈接,將每一個 Client 的鏈接數收斂到 1,這樣當 Client 數量增加時,只須要擴容 SessionServer 集羣就能夠了。 因此從設計初衷上咱們就可以看出來 SessionServer 必需要知足的兩個主要能力:從 DataServer 獲取服務信息數據;以及保存與 Client 的會話。
MetaServer 在 SOFARegistry 中,承擔着集羣元數據管理的角色,用來維護集羣成員列表,能夠認爲是 SOFARegistry 註冊中心的註冊中心。
MetaServer 做爲 SOFARegistry 的元數據中心,其核心功能能夠歸納爲:集羣成員列表管理。好比:
當 SessionServer 和 DataServer 須要知道集羣列表,而且須要擴縮容時,MetaServer 將會提供相應的數據。
其內部架構以下圖所示:
MetaServer 基於 Bolt, 經過 TCP 私有協議的形式對外提供服務,包括 DataServer, SessionServer 等,處理節點的註冊,續約和列表查詢等請求。
同時也基於 Http 協議提供控制接口,好比能夠控制 session 節點是否開啓變動通知, 健康檢查接口等。
成員列表數據存儲在 Repository 中,Repository 被一致性協議層進行包裝,做爲 SOFAJRaft 的狀態機實現,全部對 Repository 的操做都會同步到其餘節點, 經過Rgistry來操做存儲層。
MetaServer 使用 Raft 協議保證高可用和數據一致性, 同時也會保持與註冊的節點的心跳,對於心跳超時沒有續約的節點進行驅逐,來保證數據的有效性。
在可用性方面,只要未超過半數節點掛掉,集羣均可以正常對外提供服務,半數以上掛掉,Raft 協議沒法選主和日誌複製,所以沒法保證註冊的成員數據的一致性和有效性。整個集羣不可用 不會影響 Data 和 Session 節點的正常功能,只是沒法感知節點列表變化。
空談無用,just show the code。因而讓咱們帶着問題來思考,即從宏觀和微觀角度來思考MetaServer應該實現什麼功能,具體是怎麼實現的。
思考:
下面咱們就一一分析。
咱們在 sofa-registry-5.4.2/server/server/meta/src/main/java/com/alipay/sofa/registry/server/meta
看看目錄和文件結構。
按照目錄咱們能夠大體瞭解功能
具體代碼結構以下:
. ├── MetaApplication.java ├── bootstrap │ ├── AbstractNodeConfigBean.java │ ├── EnableMetaServer.java │ ├── MetaServerBootstrap.java │ ├── MetaServerConfig.java │ ├── MetaServerConfigBean.java │ ├── MetaServerConfiguration.java │ ├── MetaServerInitializerConfiguration.java │ ├── NodeConfig.java │ ├── NodeConfigBeanProperty.java │ └── ServiceFactory.java ├── executor │ └── ExecutorManager.java ├── listener │ ├── DataNodeChangePushTaskListener.java │ ├── PersistenceDataChangeNotifyTaskListener.java │ ├── ReceiveStatusConfirmNotifyTaskListener.java │ └── SessionNodeChangePushTaskListener.java ├── node │ ├── DataNodeService.java │ ├── MetaNodeService.java │ ├── NodeOperator.java │ ├── NodeService.java │ ├── SessionNodeService.java │ └── impl │ ├── DataNodeServiceImpl.java │ ├── MetaNodeServiceImpl.java │ └── SessionNodeServiceImpl.java ├── registry │ ├── MetaServerRegistry.java │ └── Registry.java ├── remoting │ ├── DataNodeExchanger.java │ ├── MetaClientExchanger.java │ ├── MetaServerExchanger.java │ ├── RaftExchanger.java │ ├── SessionNodeExchanger.java │ ├── connection │ │ ├── DataConnectionHandler.java │ │ ├── MetaConnectionHandler.java │ │ ├── NodeConnectManager.java │ │ └── SessionConnectionHandler.java │ └── handler │ ├── AbstractServerHandler.java │ ├── DataNodeHandler.java │ ├── FetchProvideDataRequestHandler.java │ ├── GetNodesRequestHandler.java │ ├── RenewNodesRequestHandler.java │ └── SessionNodeHandler.java ├── repository │ ├── NodeConfirmStatusService.java │ ├── NodeRepository.java │ ├── RepositoryService.java │ ├── VersionRepositoryService.java │ ├── annotation │ │ └── RaftAnnotationBeanPostProcessor.java │ └── service │ ├── DataConfirmStatusService.java │ ├── DataRepositoryService.java │ ├── MetaRepositoryService.java │ ├── SessionConfirmStatusService.java │ ├── SessionRepositoryService.java │ └── SessionVersionRepositoryService.java ├── resource │ ├── BlacklistDataResource.java │ ├── DecisionModeResource.java │ ├── HealthResource.java │ ├── MetaDigestResource.java │ ├── MetaStoreResource.java │ ├── PersistentDataResource.java │ ├── RenewSwitchResource.java │ └── StopPushDataResource.java ├── store │ ├── DataStoreService.java │ ├── MetaStoreService.java │ ├── RenewDecorate.java │ ├── SessionStoreService.java │ └── StoreService.java └── task ├── AbstractMetaServerTask.java ├── Constant.java ├── DataNodeChangePushTask.java ├── MetaServerTask.java ├── PersistenceDataChangeNotifyTask.java ├── ReceiveStatusConfirmNotifyTask.java ├── SessionNodeChangePushTask.java └── processor ├── DataNodeSingleTaskProcessor.java ├── MetaNodeSingleTaskProcessor.java └── SessionNodeSingleTaskProcessor.java 16 directories, 75 files
啓動能夠參考 https://www.sofastack.tech/projects/sofa-registry/server-quick-start/
SOFARegistry 支持兩種部署模式,分別是集成部署模式及獨立部署模式。
啓動命令:sh bin/startup.sh
雙擊 bin 目錄下的 startup.bat 運行文件。
經過下列log咱們能夠看到啓動信息。
MetaApplication
[2020-09-12 20:23:05,463][INFO][main][MetaServerBootstrap] - Open meta server port 9612 success! [2020-09-12 20:23:08,198][INFO][main][MetaServerBootstrap] - Open http server port 9615 success! [2020-09-12 20:23:10,298][INFO][main][MetaServerBootstrap] - Raft server port 9614 start success!group RegistryGroup [2020-09-12 20:23:10,322][INFO][main][MetaServerInitializerConfiguration] - Started MetaServer
DataApplication
[2020-09-12 20:23:25,004][INFO][main][DataServerBootstrap] - Open http server port 9622 success! [2020-09-12 20:23:26,084][INFO][main][DataServerBootstrap] - start server success [2020-09-12 20:23:26,094][INFO][main][DataApplication] - Started DataApplication in 10.217 seconds (JVM running for 11.316)
SessionApplication
[2020-09-12 20:23:50,243][INFO][main][SessionServerBootstrap] - Open http server port 9603 success! [2020-09-12 20:23:50,464][INFO][main][SessionServerInitializer] - Started SessionServer [2020-09-12 20:23:50,526][INFO][main][SessionApplication] - Started SessionApplication in 12.516 seconds (JVM running for 13.783)
各個 Server 的默認端口分別爲:
meta.server.sessionServerPort=9610 meta.server.dataServerPort=9611 meta.server.metaServerPort=9612 meta.server.raftServerPort=9614 meta.server.httpServerPort=9615
可訪問三個角色提供的健康監測 API,或查看日誌 logs/registry-startup.log:
# 查看meta角色的健康檢測接口: $ curl http://localhost:9615/health/check {"success":true,"message":"... raftStatus:Leader"} # 查看data角色的健康檢測接口: $ curl http://localhost:9622/health/check {"success":true,"message":"... status:WORKING"} # 查看session角色的健康檢測接口: $ curl http://localhost:9603/health/check {"success":true,"message":"..."}
在這裏咱們能夠看出來各類集羣是如何搭建,以及如何在集羣內部通信,分佈式協調。
按照常理來講,每一個集羣都應該依賴zookeeper之類的軟件來進行本身內部的協調,好比統一命名服務、狀態同步服務、集羣管理、分佈式應用配置項。但實際上咱們沒有發現相似的使用。
具體看配置文件發現,每臺機器都要設置全部的metaServer的host。這說明Data Server, Session Server則強依賴Meta Server。
實際上,MetaServer 使用 Raft 協議保證高可用和數據一致性, 同時也會保持與註冊的節點的心跳,對於心跳超時沒有續約的節點進行驅逐,來保證數據的有效性。Meta 層可以感知到 Session 節點和 Data 節點的變化,並通知集羣的其它節點。
這就涉及到各個角色的 failover 機制:
每臺機器在部署時須要修改 conf/application.properties 配置:
# 將3臺meta機器的ip或hostname配置到下方(填入的hostname會被內部解析爲ip地址) nodes.metaNode=DefaultDataCenter:<meta_hostname1>,<meta_hostname2>,<meta_hostname3> nodes.localDataCenter=DefaultDataCenter
每臺機器在部署時須要修改 conf/application.properties 配置:
# 將3臺 meta 機器的 ip 或 hostname 配置到下方(填入的 hostname 會被內部解析爲 ip 地址) nodes.metaNode=DefaultDataCenter:<meta_hostname1>,<meta_hostname2>,<meta_hostname3> nodes.localDataCenter=DefaultDataCenter data.server.numberOfReplicas=1000
每臺機器在部署時須要修改 conf/application.properties 配置:
# 將3臺 meta 機器的 ip 或 hostname 配置到下方(填入的 hostname 會被內部解析爲 ip 地址) nodes.metaNode=DefaultDataCenter:<meta_hostname1>,<meta_hostname2>,<meta_hostname3> nodes.localDataCenter=DefaultDataCenter nodes.localRegion=DefaultZone
MetaServer 在啓動時,會啓動三個 Bolt Server,而且註冊 Processor Handler,處理對應的請求:
而後啓動 HttpServer, 用於處理 Admin 請求,提供推送開關,集羣數據查詢等 Http 接口。
最後啓動 Raft 服務, 每一個節點同時做爲 RaftClient 和 RaftServer, 用於集羣間的變動和數據同步。
MetaServer 是一個SpringBootApplication,主要起做用的就是EnableMetaServer。
@EnableMetaServer @SpringBootApplication public class MetaApplication { public static void main(String[] args) { SpringApplication.run(MetaApplication.class, args); } }
具體參見下圖
+-------------------+ | @EnableMetaServer | | | | MetaApplication | +-------------------+
EnableMetaServer註解引入了MetaServerConfiguration
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(MetaServerConfiguration.class) public @interface EnableMetaServer { }
MetaServerConfiguration是各類配置,而且引入 MetaServerInitializerConfiguration負責啓動。
@Configuration @Import(MetaServerInitializerConfiguration.class) @EnableConfigurationProperties public class MetaServerConfiguration { }
因此咱們能夠知道:
因而程序總結結構演化爲下圖:
(Init) +------------------------------------+ | MetaServerInitializerConfiguration | +-------------+----------------------+ ^ +-------------------+ | | @EnableMetaServer | | | | | | MetaApplication | | +-------------+-----+ | (Configuration) | +---------+---------------+ +--------------> | MetaServerConfiguration | +-------------------------+
下面咱們開始引入配置。
MetaServer 模塊的各個 bean 在 JavaConfig 中統一配置,JavaConfig 類爲 MetaServerConfiguration。
MetaServerConfiguration 具體有如下幾類配置:
具體縮略版代碼以下 :
@Configuration @Import(MetaServerInitializerConfiguration.class) @EnableConfigurationProperties public class MetaServerConfiguration { @Bean @ConditionalOnMissingBean public MetaServerBootstrap metaServerBootstrap() { } @Configuration protected static class MetaServerConfigBeanConfiguration { } @Configuration public static class MetaServerServiceConfiguration { } @Configuration public static class MetaServerRepositoryConfiguration { } @Configuration public static class MetaServerRemotingConfiguration { } @Configuration public static class ResourceConfiguration { } @Configuration public static class MetaServerTaskConfiguration { } @Configuration public static class ExecutorConfiguation { } @Configuration public static class MetaDBConfiguration { } }
咱們的圖進化以下:
(Init) +------------------------------------+ | MetaServerInitializerConfiguration | +-------------+----------------------+ +---------------------+ ^ +--> | MetaServerBootstrap | +-------------------+ | | +---------------------+ | @EnableMetaServer | | | +---------------------------------+ | | | +--> |MetaServerConfigBeanConfiguration| | MetaApplication | | | +---------------------------------+ +--------------+----+ | | +------------------------------+ | | +--> |MetaServerServiceConfiguration| | | | +------------------------------+ | | | +---------------------------------+ | | +--> |MetaServerRepositoryConfiguration| | | | +---------------------------------+ | | (Configuration) | +-------------------------------+ | +---------+---------------+ +--> |MetaServerRemotingConfiguration| +--------------> | MetaServerConfiguration | +-----+ +-------------------------------+ +-------------------------+ | +----------------------+ +--> |ResourceConfiguration | | +----------------------+ | +---------------------------+ +--> |MetaServerTaskConfiguration| | +---------------------------+ | +---------------------+ +--> |ExecutorConfiguation | | +---------------------+ | +--------------------+ +--> |MetaDBConfiguration | +--------------------+
下圖爲了手機閱讀
這裏要特殊提一下handler的配置,由於這是後續分析的主體之一,是三個 Bolt Server的handler。
@Configuration public static class MetaServerRemotingConfiguration { @Bean public Exchange boltExchange() { return new BoltExchange(); } @Bean public Exchange jerseyExchange() { return new JerseyExchange(); } @Bean(name = "sessionServerHandlers") public Collection<AbstractServerHandler> sessionServerHandlers() { Collection<AbstractServerHandler> list = new ArrayList<>(); list.add(sessionConnectionHandler()); list.add(sessionNodeHandler()); list.add(renewNodesRequestHandler()); list.add(getNodesRequestHandler()); list.add(fetchProvideDataRequestHandler()); return list; } @Bean(name = "dataServerHandlers") public Collection<AbstractServerHandler> dataServerHandlers() { Collection<AbstractServerHandler> list = new ArrayList<>(); list.add(dataConnectionHandler()); list.add(getNodesRequestHandler()); list.add(dataNodeHandler()); list.add(renewNodesRequestHandler()); list.add(fetchProvideDataRequestHandler()); return list; } @Bean(name = "metaServerHandlers") public Collection<AbstractServerHandler> metaServerHandlers() { Collection<AbstractServerHandler> list = new ArrayList<>(); list.add(metaConnectionHandler()); list.add(getNodesRequestHandler()); return list; } }
因而咱們的整體架構進化具體見下圖
(Init) +------------------------------------+ | MetaServerInitializerConfiguration | +--------------+---------------------+ ^ | | | | | +---------------------+ | +--> | MetaServerBootstrap | +-------------------+ | | +---------------------+ | @EnableMetaServer | | | +---------------------------------+ | | | +--> |MetaServerConfigBeanConfiguration| | MetaApplication | | | +---------------------------------+ +--------------+----+ | | +------------------------------+ +-----------------------+ | | +--> |MetaServerServiceConfiguration| +---> | sessionServerHandlers | | | | +------------------------------+ | +-----------------------+ | | | +---------------------------------+ | +--------------------+ | | +--> |MetaServerRepositoryConfiguration+-------> | dataServerHandlers | | | | +---------------------------------+ | +--------------------+ | | (Configuration) | +-------------------------------+ | +--------------------+ | +---------+---------------+ +--> |MetaServerRemotingConfiguration| +---> | metaServerHandlers | +-----------> | MetaServerConfiguration | +-----+ +-------------------------------+ +--------------------+ +-------------------------+ | +----------------------+ +--> |ResourceConfiguration | | +----------------------+ | +---------------------------+ +--> |MetaServerTaskConfiguration| | +---------------------------+ | +---------------------+ +--> |ExecutorConfiguation | | +---------------------+ | +--------------------+ +--> |MetaDBConfiguration | +--------------------+
手機上閱讀以下:
關於handler配置,進一步細化以下圖
(Init) +--> sessionConnectionHandler | | +--> sessionNodeHandler +-----------------------+ | +---> | sessionServerHandlers +--+ +---------------------+ | +-----------------------+ +--> renewNodesRequestHandler +--> | MetaServerBootstrap | | | | +---------------------+ | | | +---------------------------------+ | +--> getNodesRequestHandler +--> |MetaServerConfigBeanConfiguration| | | | +---------------------------------+ | | | +------------------------------+ | +--> fetchProvideDataRequestHandler +--> |MetaServerServiceConfiguration| | | +------------------------------+ | | +---------------------------------+ | +--> dataConnectionHandler +--> |MetaServerRepositoryConfiguration+---+ | | +---------------------------------+ | | | +-------------------------------+ | +--------------------+ +--> getNodesRequestHandler +-------------------------+ +--> |MetaServerRemotingConfiguration| +---> | dataServerHandlers +-----+ | MetaServerConfiguration | +-----+ +-------------------------------+ | +--------------------+ | +-------------------------+ | +----------------------+ | +--> dataNodeHandler +--> |ResourceConfiguration | | | | +----------------------+ | | | +---------------------------+ | +--> renewNodesRequestHandler +--> |MetaServerTaskConfiguration| | | | +---------------------------+ | | | +---------------------+ | +--> fetchProvideDataRequestHandler +--> |ExecutorConfiguation | | | +---------------------+ | | +--------------------+ | +--> |MetaDBConfiguration | | +---> metaConnectionHandler +--------------------+ | +--------------------+ | +----> | metaServerHandlers +-----+ +--------------------+ +---> getNodesRequestHandler
手機上如圖
這個就對應了參考中的圖例:
MetaServer 在啓動時,會啓動三個 Bolt Server,而且註冊 Processor Handler,處理對應的請求:
系統是經過對MetaServerBootstrap的控制來完成了啓動。
MetaServer 模塊的各個 bean 在 JavaConfig 中統一配置,JavaConfig 類爲 MetaServerConfiguration。
啓動入口類爲 MetaServerInitializerConfiguration,該類不禁 JavaConfig 管理配置,而是繼承了 SmartLifecycle 接口,在啓動時由 Spring 框架調用其 start 方法。
public class MetaServerInitializerConfiguration implements SmartLifecycle { @Autowired private MetaServerBootstrap metaServerBootstrap; @Override public void start() { metaServerBootstrap.start(); MetaServerInitializerConfiguration.this.running.set(true); } @Override public void stop() { this.running.set(false); metaServerBootstrap.destroy(); } }
具體見下圖,由於 metaServerBootstrap 是經過配置生成,因此init過程指向配置部分。
(Init) +------------------------------------+ start,stop | MetaServerInitializerConfiguration +----------------+ +--------------+---------------------+ | ^ | | | | | | | | v | +---------------------+ | +--> | MetaServerBootstrap | +-------------------+ | | +---------------------+ | @EnableMetaServer | | | +---------------------------------+ | | | +--> |MetaServerConfigBeanConfiguration| | MetaApplication | | | +---------------------------------+ +--------------+----+ | | +------------------------------+ +-----------------------+ | | +--> |MetaServerServiceConfiguration| +---> | sessionServerHandlers | | | | +------------------------------+ | +-----------------------+ | | | +---------------------------------+ | +--------------------+ | | +--> |MetaServerRepositoryConfiguration+-------> | dataServerHandlers | | | | +---------------------------------+ | +--------------------+ | | (Configuration) | +-------------------------------+ | +--------------------+ | +---------+---------------+ +--> |MetaServerRemotingConfiguration| +---> | metaServerHandlers | +-----------> | MetaServerConfiguration | +-----+ +-------------------------------+ +--------------------+ +-------------------------+ | +----------------------+ +--> |ResourceConfiguration | | +----------------------+ | +---------------------------+ +--> |MetaServerTaskConfiguration| | +---------------------------+ | +---------------------+ +--> |ExecutorConfiguation | | +---------------------+ | +--------------------+ +--> |MetaDBConfiguration | +--------------------+
手機上以下
MetaServerBootstrap是核心啓動類,該類主要包含了三類組件:外部節點通訊組件、Raft 服務通訊組件、定時器組件。
外部節點通訊組件:在該類中有幾個 Server 通訊對象,用於和其它外部節點進行通訊。其中 httpServer 主要提供一系列 http 接口,用於 dashboard 管理、數據查詢等;sessionServer 主要是處理一些session相關的服務;dataServer 則負責數據相關服務;metaServer 負責meta server的註冊;
Raft 服務 :用於集羣間的變動和數據同步,raftExchanger 就起到這個做用;
定時器組件:例如定時檢測節點信息、定時檢測數據版本信息;具體可見 ExecutorManager,這是一個啓動各類管理線程的地方。他的啓動設置是在 MetaServerBootstrap.initRaft 之中 。
MetaServerBootstrap的定義以下:
public class MetaServerBootstrap { @Autowired private MetaServerConfig metaServerConfig; @Autowired private Exchange boltExchange; @Autowired private Exchange jerseyExchange; @Autowired private ExecutorManager executorManager; @Resource(name = "sessionServerHandlers") private Collection<AbstractServerHandler> sessionServerHandlers; @Resource(name = "dataServerHandlers") private Collection<AbstractServerHandler> dataServerHandlers; @Resource(name = "metaServerHandlers") private Collection<AbstractServerHandler> metaServerHandlers; @Autowired private ResourceConfig jerseyResourceConfig; @Autowired private ApplicationContext applicationContext; @Autowired private RaftExchanger raftExchanger; private Server sessionServer; private Server dataServer; private Server metaServer; private Server httpServer; }
能夠參見下圖
+-----------------+ +----> | metaServerConfig| | +-----------------+ | +--------------+ +----> | boltExchange | | +--------------+ | +--------------+ +----> |jerseyExchange| | +--------------+ | +---------------+ +----> |executorManager| | +---------------+ | +---------------------+ +----> |sessionServerHandlers| | +---------------------+ | +-----------------+ +----> |dataServerHandler| (Init) | +-----------------+ +------------------------------------+ start,stop +---------------------+ | +------------------+ | MetaServerInitializerConfiguration +-------------> | MetaServerBootstrap | +--------> |metaServerHandlers| +------------------------------------+ +---------------------+ | +------------------+ | +-------------+ +----> |raftExchanger| | +-------------+ | +-------------+ +----> |sessionServer| | +-------------+ | +-----------+ +----> |dataServer | | +-----------+ | +-----------+ +----> |metaServer | | +-----------+ | +----------+ +----> |httpServer| | +----------+ | +---------------------+ +----> |jerseyResourceConfig | | +---------------------+ | +-------------------+ +----> |applicationContext | +-------------------+
手機參見下圖
由於前面代碼中有
@Autowired private Exchange boltExchange; @Autowired private Exchange jerseyExchange;
這裏要特殊說明下Exchange。
Exchange 做爲 Client / Server 鏈接的抽象,負責節點之間的鏈接。在創建鏈接中,能夠設置一系列應對不一樣任務的 handler (稱之爲 ChannelHandler),這些 ChannelHandler 有的做爲 Listener 用來處理鏈接事件,有的做爲 Processor 用來處理各類指定的事件,好比服務信息數據變化、Subscriber 註冊等事件。
圖 - 每一層各司其職,協同實現節點通訊
各類節點在啓動的時候,利用 Exchange 設置了一系列 ChannelHandler,好比:
private void openDataRegisterServer() { try { if (dataStart.compareAndSet(false, true)) { dataServer = boltExchange.open(new URL(NetUtil.getLocalAddress().getHostAddress(), metaServerConfig.getDataServerPort()), dataServerHandlers .toArray(new ChannelHandler[dataServerHandlers.size()])); } } }
前面已經提到,啓動入口類爲 MetaServerInitializerConfiguration,該類不禁 JavaConfig 管理配置,而是繼承了 SmartLifecycle 接口,在啓動時由 Spring 框架調用其 start 方法。
該方法中調用了 MetaServerBootstrap # start 方法,用於啓動一系列的初始化服務。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.SmartLifecycle; public class MetaServerInitializerConfiguration implements SmartLifecycle { @Autowired private MetaServerBootstrap metaServerBootstrap;‘ @Override public void start() { metaServerBootstrap.start(); MetaServerInitializerConfiguration.this.running.set(true); } }
前面提到,MetaServerBootstrap 在啓動時,會啓動三個 Bolt Server,而且註冊 Processor Handler,處理對應的請求:
而後啓動 HttpServer, 用於處理 Admin 請求,提供推送開關,集羣數據查詢等 Http 接口。
最後啓動 Raft 服務, 每一個節點同時做爲 RaftClient 和 RaftServer, 用於集羣間的變動和數據同步。爲支持高可用特性,對於 MetaServer 來講,存儲了 SOFARegistry 的元數據,爲了保障 MetaServer 集羣的一致性,其採用了 Raft 協議來進行選舉和複製。
具體代碼參見:
public void start() { openSessionRegisterServer(); openDataRegisterServer(); openMetaRegisterServer(); openHttpServer(); initRaft(); } private void openHttpServer() { if (httpStart.compareAndSet(false, true)) { bindResourceConfig(); httpServer = jerseyExchange.open( new URL(NetUtil.getLocalAddress().getHostAddress(), metaServerConfig .getHttpServerPort()), new ResourceConfig[] { jerseyResourceConfig }); } } private void initRaft() { raftExchanger.startRaftServer(executorManager); raftExchanger.startRaftClient(); raftExchanger.startCliService(); }
Raft 和 Bolt 是SOFA所特殊實現,因此咱們暫不介紹其底層機制,之後有機會單獨開篇,下面提一下三個BoltServer。
private void openSessionRegisterServer() { if (sessionStart.compareAndSet(false, true)) { sessionServer = boltExchange .open( new URL(NetUtil.getLocalAddress().getHostAddress(), metaServerConfig .getSessionServerPort()), sessionServerHandlers .toArray(new ChannelHandler[sessionServerHandlers.size()])); } } private void openDataRegisterServer() { if (dataStart.compareAndSet(false, true)) { dataServer = boltExchange.open(new URL(NetUtil.getLocalAddress().getHostAddress(), metaServerConfig.getDataServerPort()), dataServerHandlers .toArray(new ChannelHandler[dataServerHandlers.size()])); } } private void openMetaRegisterServer() { if (metaStart.compareAndSet(false, true)) { metaServer = boltExchange.open(new URL(NetUtil.getLocalAddress().getHostAddress(), metaServerConfig.getMetaServerPort()), metaServerHandlers .toArray(new ChannelHandler[metaServerHandlers.size()])); } }
這幾個Server的handler就是咱們前面配置的
@Resource(name = "sessionServerHandlers") private Collection<AbstractServerHandler> sessionServerHandlers; @Resource(name = "dataServerHandlers") private Collection<AbstractServerHandler> dataServerHandlers; @Resource(name = "metaServerHandlers") private Collection<AbstractServerHandler> metaServerHandlers;
具體參見下圖:
+------------------------+ +-----------------+ +---> |sessionConnectionHandler| +----> | metaServerConfig| | +------------------------+ | +-----------------+ | +-------------------+ | +--------------+ +---> |sessionNodeHandler | +----> | boltExchange | +-->+ +-------------------+ | +--------------+ | | +------------------------+ | +--------------+ | +---> |renewNodesRequestHandler| +----> |jerseyExchange| | | +------------------------+ | +--------------+ | | +----------------------+ | +---------------+ | +---> |getNodesRequestHandler| +----> |executorManager| | | +----------------------+ | +---------------+ +---------------------+ | | +------------------------------+ +-------------------------------------> |sessionServerHandlers+---+ +---> |fetchProvideDataRequestHandler| | +-------------+ +---------------------+ +------------------------------+ +----> |sessionServer| +------------------^ | +-------------+ +---------------------+ | ----> |dataConnectionHandler| | +------------------+ | +---------------------+ +---------------------+ +-------------------------------------> |dataServerHandlers+----------+ +----------------------+ | MetaServerBootstrap | +---+ +-----------+ +------------------+ +---> |getNodesRequestHandler| +---------------------+ +----> |dataServer +----------------------^ | +----------------------+ | +-----------+ | +---------------+ | +---> |dataNodeHandler| | +------------------+ | +---------------+ +------------------------------------> |metaServerHandlers+------+ | +------------------------+ | +-----------+ +------------------+ | +---> |renewNodesRequestHandler| +----> |metaServer +----------------------^ | | +------------------------+ | +-----------+ | | +------------------------------+ | | +---> |fetchProvideDataRequestHandler| | +-------------+ | +------------------------------+ +----> |raftExchanger| | | +-------------+ | | | | +----------+ | +----> |httpServer| | +---------------------+ | +----------+ | +--> |metaConnectionHandler| | +---------------------+ +----> +---------------------+ +----> |jerseyResourceConfig | | +---------------------+ | +---------------------+ +--> |getNodesRequestHandle| | +-------------------+ +---------------------+ +----> |applicationContext | +-------------------+
手機上
在初始化時候,大體堆棧以下 :
interest:55, RenewNodesRequestHandler (com.alipay.sofa.registry.server.meta.remoting.handler) interest:61, SyncUserProcessorAdapter (com.alipay.sofa.registry.remoting.bolt) registerUserProcessor:42, UserProcessorRegisterHelper (com.alipay.remoting.rpc.protocol) registerUserProcessor:376, RpcServer (com.alipay.remoting.rpc) registerUserProcessorHandler:159, BoltServer (com.alipay.sofa.registry.remoting.bolt) initHandler:139, BoltServer (com.alipay.sofa.registry.remoting.bolt) startServer:92, BoltServer (com.alipay.sofa.registry.remoting.bolt) open:65, BoltExchange (com.alipay.sofa.registry.remoting.bolt.exchange) open:36, BoltExchange (com.alipay.sofa.registry.remoting.bolt.exchange) openSessionRegisterServer:149, MetaServerBootstrap (com.alipay.sofa.registry.server.meta.bootstrap) start:108, MetaServerBootstrap (com.alipay.sofa.registry.server.meta.bootstrap) start:51, MetaServerInitializerConfiguration (com.alipay.sofa.registry.server.meta.bootstrap)
以SessionServer爲例,在構建過程當中,調用到 BoltExchange . open。
private void openSessionRegisterServer() { if (sessionStart.compareAndSet(false, true)) { sessionServer = boltExchange .open( new URL(NetUtil.getLocalAddress().getHostAddress(), metaServerConfig .getSessionServerPort()), sessionServerHandlers .toArray(new ChannelHandler[sessionServerHandlers.size()])); } }
BoltExchange中有
@Override public Server open(URL url, ChannelHandler... channelHandlers) { BoltServer server = createBoltServer(url, channelHandlers); setServer(server, url); server.startServer(); return server; }
BoltServer中有
public void startServer() { if (isStarted.compareAndSet(false, true)) { boltServer = new RpcServer(url.getPort(), true); initHandler(); boltServer.start(); } } private void initHandler() { if (initHandler.compareAndSet(false, true)) { boltServer.addConnectionEventProcessor(ConnectionEventType.CONNECT, new ConnectionEventAdapter(ConnectionEventType.CONNECT, getConnectionEventHandler(), this)); boltServer.addConnectionEventProcessor(ConnectionEventType.CLOSE, new ConnectionEventAdapter(ConnectionEventType.CLOSE, getConnectionEventHandler(), this)); boltServer.addConnectionEventProcessor(ConnectionEventType.EXCEPTION, new ConnectionEventAdapter(ConnectionEventType.EXCEPTION, getConnectionEventHandler(), this)); registerUserProcessorHandler(); } }
最後調用,會設定同步和異步的handler。
private void registerUserProcessorHandler() { if (channelHandlers != null) { for (ChannelHandler channelHandler : channelHandlers) { if (HandlerType.PROCESSER.equals(channelHandler.getType())) { if (InvokeType.SYNC.equals(channelHandler.getInvokeType())) { boltServer.registerUserProcessor(new SyncUserProcessorAdapter( channelHandler)); } else { boltServer.registerUserProcessor(new AsyncUserProcessorAdapter( channelHandler)); } } } } }
以使用 Jetty 的 openHttpServer 爲例
啓動 HttpServer, 用於處理 Admin 請求,提供推送開關,集羣數據查詢等 Http 接口。
public class JerseyJettyServer implements Server { public static org.eclipse.jetty.server.Server createServer(final URI uri, final ResourceConfig resourceConfig, final boolean start) { JettyHttpContainer handler = ContainerFactory.createContainer(JettyHttpContainer.class, resourceConfig); int defaultPort = Container.DEFAULT_HTTP_PORT; final int port = (uri.getPort() == -1) ? defaultPort : uri.getPort(); final org.eclipse.jetty.server.Server server = new org.eclipse.jetty.server.Server( new JettyConnectorThreadPool()); final ServerConnector http = new ServerConnector(server, new HttpConnectionCustomFactory()); http.setPort(port); server.setConnectors(new Connector[] { http }); if (handler != null) { server.setHandler(handler); } if (start) { try { // Start the server. server.start(); } } return server; } }
其堆棧以下:
<init>:72, JerseyJettyServer (com.alipay.sofa.registry.remoting.jersey) open:73, JerseyExchange (com.alipay.sofa.registry.remoting.jersey.exchange) open:40, JerseyExchange (com.alipay.sofa.registry.remoting.jersey.exchange) openHttpServer:205, MetaServerBootstrap (com.alipay.sofa.registry.server.meta.bootstrap) start:114, MetaServerBootstrap (com.alipay.sofa.registry.server.meta.bootstrap) start:51, MetaServerInitializerConfiguration (com.alipay.sofa.registry.server.meta.bootstrap) doStart:173, DefaultLifecycleProcessor (org.springframework.context.support) access$200:50, DefaultLifecycleProcessor (org.springframework.context.support) start:350, DefaultLifecycleProcessor$LifecycleGroup (org.springframework.context.support) startBeans:149, DefaultLifecycleProcessor (org.springframework.context.support) onRefresh:112, DefaultLifecycleProcessor (org.springframework.context.support) finishRefresh:880, AbstractApplicationContext (org.springframework.context.support) refresh:546, AbstractApplicationContext (org.springframework.context.support) refresh:693, SpringApplication (org.springframework.boot) refreshContext:360, SpringApplication (org.springframework.boot) run:303, SpringApplication (org.springframework.boot) run:1118, SpringApplication (org.springframework.boot) run:1107, SpringApplication (org.springframework.boot) main:42, MetaApplication (com.alipay.sofa.registry.server.meta)
以下存儲由Raft來保證數據一致性,後文針對此有詳細講解。
@RaftService(uniqueId = "sessionServer") public class SessionVersionRepositoryService @RaftService(uniqueId = "metaServer") public class MetaRepositoryService @RaftService(uniqueId = "dataServer") public class DataRepositoryService @RaftService(uniqueId = "sessionServer") public class SessionRepositoryService @RaftService(uniqueId = "dataServer") public class DataConfirmStatusService @RaftService(uniqueId = "sessionServer") public class SessionConfirmStatusService
是一個啓動各類管理線程的地方,都是按期常規管理任務。
public class ExecutorManager { private ScheduledExecutorService scheduler; private ThreadPoolExecutor heartbeatCheckExecutor; private ThreadPoolExecutor checkDataChangeExecutor; private ThreadPoolExecutor getOtherDataCenterChangeExecutor; private ThreadPoolExecutor connectMetaServerExecutor; private ThreadPoolExecutor checkNodeListChangePushExecutor; private ThreadPoolExecutor raftClientRefreshExecutor; private MetaServerConfig metaServerConfig; @Autowired private Registry metaServerRegistry; @Autowired private MetaClientExchanger metaClientExchanger; @Autowired private RaftExchanger raftExchanger; public void startScheduler() { init(); scheduler.schedule(new TimedSupervisorTask("HeartbeatCheck", scheduler, heartbeatCheckExecutor, metaServerConfig.getSchedulerHeartbeatTimeout(), TimeUnit.SECONDS, metaServerConfig.getSchedulerHeartbeatExpBackOffBound(), () -> metaServerRegistry.evict()), metaServerConfig.getSchedulerHeartbeatFirstDelay(), TimeUnit.SECONDS); scheduler.schedule( new TimedSupervisorTask("GetOtherDataCenterChange", scheduler, getOtherDataCenterChangeExecutor, metaServerConfig.getSchedulerGetDataChangeTimeout(), TimeUnit.SECONDS, metaServerConfig.getSchedulerGetDataChangeExpBackOffBound(), () -> { metaServerRegistry.getOtherDataCenterNodeAndUpdate(NodeType.DATA); metaServerRegistry.getOtherDataCenterNodeAndUpdate(NodeType.META); }), metaServerConfig.getSchedulerGetDataChangeFirstDelay(), TimeUnit.SECONDS); scheduler.schedule(new TimedSupervisorTask("ConnectMetaServer", scheduler, connectMetaServerExecutor, metaServerConfig.getSchedulerConnectMetaServerTimeout(), TimeUnit.SECONDS, metaServerConfig.getSchedulerConnectMetaServerExpBackOffBound(), () -> metaClientExchanger.connectServer()), metaServerConfig.getSchedulerConnectMetaServerFirstDelay(), TimeUnit.SECONDS); scheduler.schedule( new TimedSupervisorTask("CheckSessionNodeListChangePush", scheduler, checkNodeListChangePushExecutor, metaServerConfig.getSchedulerCheckNodeListChangePushTimeout(), TimeUnit.SECONDS, metaServerConfig.getSchedulerCheckNodeListChangePushExpBackOffBound(), () -> metaServerRegistry.pushNodeListChange(NodeType.SESSION)), metaServerConfig.getSchedulerCheckNodeListChangePushFirstDelay(), TimeUnit.SECONDS); scheduler.schedule( new TimedSupervisorTask("CheckDataNodeListChangePush", scheduler, checkNodeListChangePushExecutor, metaServerConfig.getSchedulerCheckNodeListChangePushTimeout(), TimeUnit.SECONDS, metaServerConfig.getSchedulerCheckNodeListChangePushExpBackOffBound(), () -> metaServerRegistry.pushNodeListChange(NodeType.DATA)), metaServerConfig.getSchedulerCheckNodeListChangePushFirstDelay(), TimeUnit.SECONDS); scheduler.schedule(new TimedSupervisorTask("RaftClientRefresh", scheduler, raftClientRefreshExecutor, metaServerConfig.getSchedulerCheckNodeListChangePushTimeout(), TimeUnit.SECONDS, metaServerConfig.getSchedulerCheckNodeListChangePushExpBackOffBound(), () -> raftExchanger.refreshRaftClient()), metaServerConfig.getSchedulerCheckNodeListChangePushFirstDelay(), TimeUnit.SECONDS); } }
ExecutorManager 的啓動設置是在 MetaServerBootstrap.initRaft 之中,分別啓動RaftServer,RaftClient,CliService。
private void initRaft() { raftExchanger.startRaftServer(executorManager); raftExchanger.startRaftClient(); raftExchanger.startCliService(); }
當 Raft 選出 Leader 以後,會調用到 ExecutorManager # startScheduler。
TimedSupervisorTask 實現了 TimerTask。
public class TimedSupervisorTask extends TimerTask { private final ScheduledExecutorService scheduler; private final ThreadPoolExecutor executor; private final long timeoutMillis; private final Runnable task; private String name; private final AtomicLong delay; private final long maxDelay; @Override public void run() { Future future = null; try { future = executor.submit(task); // block until done or timeout future.get(timeoutMillis, TimeUnit.MILLISECONDS); delay.set(timeoutMillis); } catch { ..... } finally { if (future != null) { future.cancel(true); } scheduler.schedule(this, delay.get(), TimeUnit.MILLISECONDS); } } }
能夠看到管理任務大體有:
TimedSupervisorTask 會按期執行,好比 CheckDataNodeListChangePush 這個線程會按期執行 metaServerRegistry.pushNodeListChange(NodeType.DATA)) 來看看是否有變化。這裏就會用到DataNode註冊時候,Confirm的消息。
@Override public void pushNodeListChange() { NodeOperator<DataNode> fireNode; if ((fireNode = dataConfirmStatusService.peekConfirmNode()) != null) { NodeChangeResult nodeChangeResult = getNodeChangeResult(); Map<String, Map<String, DataNode>> map = nodeChangeResult.getNodes(); Map<String, DataNode> addNodes = map.get(nodeConfig.getLocalDataCenter()); if (addNodes != null) { Map<String, DataNode> previousNodes = dataConfirmStatusService.putExpectNodes( fireNode.getNode(), addNodes); if (!previousNodes.isEmpty()) { firePushDataListTask(fireNode, nodeChangeResult, previousNodes, true); } } firePushSessionListTask(nodeChangeResult, fireNode.getNodeOperate().toString()); } }
再好比按期去除過時的Node:
public class MetaServerRegistry implements Registry<Node> { @Override public void evict() { for (NodeType nodeType : NodeType.values()) { StoreService storeService = ServiceFactory.getStoreService(nodeType); if (storeService != null) { Collection<Node> expiredNodes = storeService.getExpired(); if (expiredNodes != null && !expiredNodes.isEmpty()) { storeService.removeNodes(expiredNodes); } } } } }
ServiceFactory 須要特殊說明,它提供了系統所須要的一系列服務。特殊之處在於,ServiceFactory 不是由 MetaServerBootstrap 負責啓動,而是由 Spring 負責啓動。由於 ServiceFactory 繼承了ApplicationContextAware,因此啓動時候生成。
在Web應用中,Spring容器一般採用聲明式方式配置產生:開發者只要在web.xml中配置一個Listener,該Listener將會負責初始化Spring容器,MVC框架能夠直接調用Spring容器中的Bean,無需訪問Spring容器自己。在這種狀況下,容器中的Bean處於容器管理下,無需主動訪問容器,只需接受容器的依賴注入便可。
但在某些特殊的狀況下,Bean須要實現某個功能,但該功能必須藉助於Spring容器才能實現,此時就必須讓該Bean先獲取Spring容器,而後藉助於Spring容器實現該功能。爲了讓Bean獲取它所在的Spring容器,可讓該Bean實現ApplicationContextAware接口。
下面代碼能夠看出來,啓動了一系列服務。
public class ServiceFactory implements ApplicationContextAware { private static Map<NodeType, StoreService> storeServiceMap = new HashMap<>(); private static Map<NodeType, NodeConnectManager> connectManagerMap = new HashMap<>(); private static Map<NodeType, NodeService> nodeServiceMap = new HashMap<>(); } storeServiceMap = {HashMap@5107} size = 3 {Node$NodeType@5525} "SESSION" -> {SessionStoreService@5526} key = {Node$NodeType@5525} "SESSION" value = {SessionStoreService@5526} {Node$NodeType@4815} "DATA" -> {DataStoreService@5527} key = {Node$NodeType@4815} "DATA" value = {DataStoreService@5527} {Node$NodeType@5528} "META" -> {MetaStoreService@5529} key = {Node$NodeType@5528} "META" value = {MetaStoreService@5529} connectManagerMap = {HashMap@5532} size = 3 {Node$NodeType@5525} "SESSION" -> {SessionConnectionHandler@5548} key = {Node$NodeType@5525} "SESSION" value = {SessionConnectionHandler@5548} {Node$NodeType@4815} "DATA" -> {DataConnectionHandler@5549} key = {Node$NodeType@4815} "DATA" value = {DataConnectionHandler@5549} {Node$NodeType@5528} "META" -> {MetaConnectionHandler@5550} key = {Node$NodeType@5528} "META" value = {MetaConnectionHandler@5550} nodeServiceMap = {HashMap@5533} size = 3 {Node$NodeType@5525} "SESSION" -> {SessionNodeServiceImpl@5540} key = {Node$NodeType@5525} "SESSION" value = {SessionNodeServiceImpl@5540} {Node$NodeType@4815} "DATA" -> {DataNodeServiceImpl@5541} key = {Node$NodeType@4815} "DATA" value = {DataNodeServiceImpl@5541} {Node$NodeType@5528} "META" -> {MetaNodeServiceImpl@5542} key = {Node$NodeType@5528} "META" value = {MetaNodeServiceImpl@5542}
至此,MetaServer的架構和啓動介紹完成,咱們下文將會介紹基本功能,好比註冊,存儲,續約等。
服務註冊中心 MetaServer 功能介紹和實現剖析 | SOFARegistry 解析
服務註冊中心如何實現 DataServer 平滑擴縮容 | SOFARegistry 解析
服務註冊中心數據一致性方案分析 | SOFARegistry 解析
服務註冊中心如何實現秒級服務上下線通知 | SOFARegistry 解析
服務註冊中心 Session 存儲策略 | SOFARegistry 解析
服務註冊中心數據分片和同步方案詳解 | SOFARegistry 解析
服務註冊中心 SOFARegistry 解析 | 服務發現優化之路
海量數據下的註冊中心 - SOFARegistry 架構介紹
詳解螞蟻金服 SOFAJRaft | 生產級高性能 Java 實現
怎樣打造一個分佈式數據庫——rocksDB, raft, mvcc,本質上是爲了解決跨數據中心的複製