天天看點

源碼 狀态機_[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

  • 0x00 摘要
  • 0x01 服務注冊中心
    • 1.1 服務注冊中心簡介
    • 1.2 SOFARegistry 總體架構
    • 1.3 為什麼要分層
  • 0x02 MetaServer
    • 2.1簡介
    • 2.2 問題
  • 0x03 代碼結構
  • 0x04 啟動運作
    • 4.1 內建部署
    • 4.2 獨立部署
  • 0x05 總體邏輯
    • 5.1 程式主體
    • 5.2 配置
  • 0x06 啟動
    • 6.1 架構
    • 6.2 類定義
    • 6.3 通信 Exchange
    • 6.4 啟動入口
    • 6.4.1 啟動服務Server
    • 6.4.2 ExecutorManager
    • 6.4.3 ServiceFactory
  • 0xFF 參考

0x00 摘要

SOFARegistry 是螞蟻金服開源的一個生産級、高時效、高可用的服務注冊中心。本系列将帶領大家一起分析其MetaServer的實作機制,本文為第一篇,介紹MetaServer總體架構。

本系列總體參考了官方的部落格,具體請參見"0xFF 參考"。大家可以把參考作為總綱,我這系列文章作為注釋補遺翻閱。

0x01 服務注冊中心

1.1 服務注冊中心簡介

在微服務架構下,一個網際網路應用的服務端背後往往存在大量服務間的互相調用。例如服務 A 在鍊路上依賴于服務 B,那麼在業務發生時,服務 A 需要知道服務 B 的位址,才能完成服務調用。而分布式架構下,每個服務往往都是叢集部署的,叢集中的機器也是經常變化的,是以服務 B 的位址不是固定不變的。如果要保證業務的可靠性,服務調用者則需要感覺被調用服務的位址變化。

既然成千上萬的服務調用者都要感覺這樣的變化,那這種感覺能力便下沉成為微服務中一種固定的架構模式:服務注冊中心。

服務注冊中心裡,有服務提供者和服務消費者兩種重要的角色,服務調用方是消費者,服務被調方是提供者。對于同一台機器,往往兼具兩者角色,既被其它服務調用,也調用其它服務。服務提供者将自身提供的服務資訊釋出到服務注冊中心,服務消費者通過訂閱的方式感覺所依賴服務的資訊是否發生變化。

服務注冊中心在服務調用的場景中,扮演一個“中介”的角色,服務釋出者 (Publisher) 将服務釋出到服務注冊中心,服務調用方 (Subscriber) 通過通路服務注冊中心就能夠擷取到服務資訊,進而實作調用。

Subscriber 在第一次調用服務時,會通過 Registry 找到相應的服務的 IP 位址清單,通過負載均衡算法從 IP 清單中取一個服務提供者的伺服器調用服務。同時 Subscriber 會将 Publisher 的服務清單資料緩存到本地,供後續使用。當 Subscriber 後續再調用 Publisher 時,優先使用緩存的位址清單,不需要再去請求Registry。

服務注冊中心Registry的最主要能力是服務注冊和服務發現兩個過程。

  • 服務注冊的過程最重要是對服務釋出的資訊進行存儲。
  • 服務發現的過程是把服務釋出端的所有變化(包括節點變化和服務資訊變化)及時準确的通知到訂閱方的過程。

1.2 SOFARegistry 總體架構

1.2.1 分層

SOFARegistry 作為服務注冊中心,分為4個層,分别為:

  • Client 層

Client 層是應用伺服器叢集。Client 層是應用層,每個應用系統通過依賴注冊中心相關的用戶端 jar 包,通過程式設計方式來使用服務注冊中心的服務釋出和服務訂閱能力。

  • Session 層

Session層是伺服器叢集。顧名思義,Session 層是會話層,通過長連接配接和 Client 層的應用伺服器保持通訊,負責接收 Client 的服務釋出和服務訂閱請求。

在服務注冊中心的服務端因為每個存儲節點對應的用戶端的連結資料量有限,必須進行特殊的一層劃分用于專門收斂無限擴充的用戶端連接配接,然後在透傳相應的請求到存儲層。

該層隻在記憶體中儲存各個服務的釋出訂閱關系,對于具體的服務資訊,隻在 Client 層和 Data 層之間透傳轉發。Session 層是一個無資料狀态的代理層,可以随着 Client 層應用規模的增長而擴容。

因為 SOFARegistry 的服務發現需要較高的時效性,對外表現為主動推送變更到用戶端,是以推送的主體實作也集中在 Session 層,内部的推拉結合主要是通過 Data 存儲層的資料版本變更推送到所有 Session 節點,各個 Session 節點根據存儲的訂閱關系和首次訂閱擷取的資料版本資訊進行比對,最終确定推送給那些服務消費方用戶端。

  • Data 層

資料伺服器叢集。Data 層通過分片存儲的方式儲存着所用應用的服務注冊資料。資料按照 dataInfoId(每一份服務資料的唯一辨別)進行一緻性 Hash 分片,多副本備份,保證資料的高可用。Data 層可以随着資料規模的增長,在不影響業務的前提下實作平滑的擴縮容。

  • Meta 層

中繼資料伺服器叢集。這個叢集管轄的範圍是 Session 伺服器叢集和 Data 伺服器叢集的伺服器資訊,其角色就相當于 SOFARegistry 架構内部的服務注冊中心,隻不過 SOFARegistry 作為服務注冊中心是服務于廣大應用服務層,而 Meta 叢集是服務于 SOFARegistry 内部的 Session 叢集和 Data 叢集,Meta 層能夠感覺到 Session 節點和 Data 節點的變化,并通知叢集的其它節點。

1.3 為什麼要分層

SOFARegistry 内部為什麼要進行資料分層,是因為系統容量的限制。

在 SOFARegistry 的應用場景中,體量龐大的資料主要有兩類:Session 資料、服務資訊資料。兩類資料的相同之處在于其資料量都會不斷擴充,而不同的是其擴充的原因并不相同:

  • Session 是對應于 Client 的連接配接,其資料量是随着業務機器規模的擴充而增長,
  • 服務資訊資料量的增長是由 Publisher 的釋出所決定。

是以 SOFARegistry 通過分層設計,将兩種資料隔離,進而使二者的擴容互不影響。

源碼 狀态機_[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

圖3 - 分層,擴容互不影響

這也是 SOFARegistry 設計三層模型的原因,通過 SessionServer 來負責與 Client 的連接配接,将每個 Client 的連接配接數收斂到 1,這樣當 Client 數量增長時,隻需要擴容 SessionServer 叢集就可以了。是以從設計初衷上我們就能夠看出來 SessionServer 必須要滿足的兩個主要能力:從 DataServer 擷取服務資訊資料;以及儲存與 Client 的會話。

0x02 MetaServer

2.1簡介

MetaServer 在 SOFARegistry 中,承擔着叢集中繼資料管理的角色,用來維護叢集成員清單,可以認為是 SOFARegistry 注冊中心的注冊中心。

MetaServer 作為 SOFARegistry 的中繼資料中心,其核心功能可以概括為:叢集成員清單管理。比如:

  • 節點清單的注冊與存儲
  • 節點清單的變更通知
  • 節點健康監測

當 SessionServer 和 DataServer 需要知道叢集清單,并且需要擴縮容時,MetaServer 将會提供相應的資料。

其内部架構如下圖所示:

源碼 狀态機_[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

内部架構圖

MetaServer 基于 Bolt, 通過 TCP 私有協定的形式對外提供服務,包括 DataServer, SessionServer 等,處理節點的注冊,續約和清單查詢等請求。

同時也基于 Http 協定提供控制接口,比如可以控制 session 節點是否開啟變更通知, 健康檢查接口等。

成員清單資料存儲在 Repository 中,Repository 被一緻性協定層進行包裝,作為 SOFAJRaft 的狀态機實作,所有對 Repository 的操作都會同步到其他節點, 通過Rgistry來操作存儲層。

MetaServer 使用 Raft 協定保證高可用和資料一緻性, 同時也會保持與注冊的節點的心跳,對于心跳逾時沒有續約的節點進行驅逐,來保證資料的有效性。

在可用性方面,隻要未超過半數節點挂掉,叢集都可以正常對外提供服務,半數以上挂掉,Raft 協定無法選主和日志複制,是以無法保證注冊的成員資料的一緻性和有效性。整個叢集不可用 不會影響 Data 和 Session 節點的正常功能,隻是無法感覺節點清單變化。

2.2 問題

空談無用,just show the code。于是讓我們帶着問題來思考,即從宏觀和微觀角度來思考MetaServer應該實作什麼功能,具體是怎麼實作的。

思考:

  • MetaServer具體是以什麼方式實作。
  • MetaServer如何實作高可用。
  • MetaServer如何實作或者應用内部通訊協定。
  • MetaServer如何保持DataNode清單和SessionNode清單。
  • MetaServer如何處理節點清單變更推送。
  • MetaServer如何處理節點清單查詢。
  • MetaServer如何處理MetaServer如何保證資料一緻性。
  • 各個叢集是如何搭建,如何完成高可用。

下面我們就一一分析。

0x03 代碼結構

我們在

sofa-registry-5.4.2/server/server/meta/src/main/java/com/alipay/sofa/registry/server/meta

看看目錄和檔案結構。

按照目錄我們可以大緻了解功能

  • MetaApplication.java :MetaServer程式主體,入口。
  • bootstrap :負責MetaServer的啟動和配置。
  • executor :負責各種定時管理任務,他的啟動設定是在 MetaServerBootstrap.initRaft  之中。
  • listener  :SOFARegistry 采用了 Handler - Task & Strategy - Listener 的方式來應對服務注冊中的各種場景和任務,這樣的處理模型能夠盡可能的讓代碼和架構清晰整潔。
  • node :對業務節點的抽象,包括DataNode,SessionNode,MetaNode。
  • registry :通過Registry來操作存儲層,所有對 Repository 的操作都會同步到其他節點。
  • remoting :對外互動接口,提供各種對外的 handler。
  • repository :叢集節點清單存儲在 Repository 中,通過 Raft 強一緻性協定對外提供節點注冊、續約、清單查詢等 Bolt 請求,進而保障叢集獲得的資料是強一緻性的。Repository 被一緻性協定層進行包裝,作為 SOFAJRaft 的狀态機實作。
  • resource :http Server的接口,用來響應控制消息。
  • store :封裝了存儲節點的操作。
  • task :封裝了異步執行邏輯,通過TaskDispatcher,TaskExecutors 來執行各種定義好的異步Task。

具體代碼結構如下:

0x04 啟動運作

啟動可以參考  https://www.sofastack.tech/projects/sofa-registry/server-quick-start/

SOFARegistry 支援兩種部署模式,分别是內建部署模式及獨立部署模式。

4.1 內建部署

4.1.1 Linux/Unix/Mac

啟動指令:

sh bin/startup.sh

4.1.2 Windows

輕按兩下 bin 目錄下的 startup.bat 運作檔案。

4.1.3 啟動資訊

通過下列log我們可以看到啟動資訊。

MetaApplication

DataApplication

SessionApplication

各個 Server 的預設端口分别為:

可通路三個角色提供的健康監測 API,或檢視日志 logs/registry-startup.log:

4.2 獨立部署

在這裡我們可以看出來各種叢集是如何搭建,以及如何在叢集内部通訊,分布式協調。

按照常理來說,每個叢集都應該依賴zookeeper之類的軟體來進行自己内部的協調,比如統一命名服務、狀态同步服務、叢集管理、分布式應用配置項。但實際上我們沒有發現類似的使用。

具體看配置檔案發現,每台機器都要設定所有的metaServer的host。這說明Data Server,  Session Server則強依賴Meta Server。

實際上,MetaServer 使用 Raft 協定保證高可用和資料一緻性, 同時也會保持與注冊的節點的心跳,對于心跳逾時沒有續約的節點進行驅逐,來保證資料的有效性。Meta 層能夠感覺到 Session 節點和 Data 節點的變化,并通知叢集的其它節點。

這就涉及到各個角色的 failover 機制:

  • MetaServer 叢集部署,内部基于 Raft 協定選舉和複制,隻要不超過 1⁄2 節點當機,就可以對外服務。
  • DataServer 叢集部署,基于一緻性 Hash 承擔不同的資料分片,資料分片擁有多個副本,一個主副本和多個備副本。如果 DataServer 當機,MetaServer 能感覺,并通知所有 DataServer 和 SessionServer,資料分片可 failover 到其他副本,同時 DataServer 叢集内部會進行分片資料的遷移。
  • SessionServer 叢集部署,任何一台 SessionServer 當機時 Client 會自動 failover 到其他 SessionServer,并且 Client 會拿到最新的 SessionServer 清單,後續不會再連接配接這台當機的 SessionServer。

4.2.1 部署meta

每台機器在部署時需要修改 conf/application.properties 配置:

# 将3台meta機器的ip或hostname配置到下方(填入的hostname會被内部解析為ip位址)
nodes.metaNode=DefaultDataCenter:,,
nodes.localDataCenter=DefaultDataCenter
           

4.2.2 部署data

每台機器在部署時需要修改 conf/application.properties 配置:

# 将3台 meta 機器的 ip 或 hostname 配置到下方(填入的 hostname 會被内部解析為 ip 位址)
nodes.metaNode=DefaultDataCenter:,,
nodes.localDataCenter=DefaultDataCenter
data.server.numberOfReplicas=1000
           

4.2.3 部署 session

每台機器在部署時需要修改 conf/application.properties 配置:

# 将3台 meta 機器的 ip 或 hostname 配置到下方(填入的 hostname 會被内部解析為 ip 位址)
nodes.metaNode=DefaultDataCenter:,,
nodes.localDataCenter=DefaultDataCenter
nodes.localRegion=DefaultZone
           

0x05 總體邏輯

MetaServer 在啟動時,會啟動三個 Bolt Server,并且注冊 Processor Handler,處理對應的請求:

  • DataServer:處理 DataNode 相關的請求;
  • SessionServer:處理 SessionNode 相關的請求;
  • MetaServer:處理MetaNode相關的請求;

然後啟動 HttpServer, 用于處理 Admin 請求,提供推送開關,叢集資料查詢等 Http 接口。

最後啟動 Raft 服務, 每個節點同時作為 RaftClient 和 RaftServer, 用于叢集間的變更和資料同步。

5.1 程式主體

MetaServer 是一個SpringBootApplication,主要起作用的就是EnableMetaServer。

具體參見下圖

EnableMetaServer注解引入了MetaServerConfiguration

MetaServerConfiguration是各種配置,并且引入 MetaServerInitializerConfiguration負責啟動。

是以我們可以知道:

  • MetaServerConfiguration :負責配置
  • MetaServerInitializerConfiguration :負責啟動

于是程式總結結構演化為下圖:

下面我們開始引入配置。

5.2 配置

MetaServer 子產品的各個 bean 在 JavaConfig 中統一配置,JavaConfig 類為 MetaServerConfiguration。

5.2.1 配置分類

MetaServerConfiguration 具體有以下幾類配置:

  • Bootstrap,負責MetaServer啟動配置,是核心啟動類。
  • ServerConfig,負責MetaServer的配置項,比如MetaServerConfig,NodeConfig,PropertySplitter。
  • ServerServiceConfiguration,負責服務相關的配置,比如 sessionNodeService,storeServiceFactory,sessionStoreService。
  • ServerRepositoryConfiguration,負責Repository相關的配置,比如dataRepositoryService,sessionRepositoryService等。
  • ServerRemotingConfiguration,負責網絡相關配置,比如BoltExchange,JerseyExchange,這裡随後會重點說明。
  • ResourceConfiguration,負責Resource相關配置,比如 jerseyResourceConfig,persistentDataResource。
  • ServerTaskConfiguration,負責各種  task 相關配置 ,比如dataNodeSingleTaskProcessor。
  • ExecutorConfiguation,ExecutorManager相關配置。
  • MetaDBConfiguration,DB相關配置。

具體縮略版代碼如下 :

我們的圖進化如下:

下圖為了手機閱讀

源碼 狀态機_[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

5.2.2 handler的配置

這裡要特殊提一下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 sessionServerHandlers() {
        Collection 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 dataServerHandlers() {
        Collection 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 metaServerHandlers() {
        Collection list = new ArrayList<>();
        list.add(metaConnectionHandler());
        list.add(getNodesRequestHandler());return list;
    }
}
           

于是我們的總體架構進化具體見下圖

手機上閱讀如下:

源碼 狀态機_[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

關于handler配置,進一步細化如下圖

手機上如圖

源碼 狀态機_[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

這個就對應了參考中的圖例:

MetaServer 在啟動時,會啟動三個 Bolt Server,并且注冊 Processor Handler,處理對應的請求:

源碼 狀态機_[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

meta-server

  • DataServer:處理 DataNode 相關的請求;
  • SessionServer:處理 SessionNode 相關的請求;
  • MetaServer:處理MetaNode相關的請求;

0x06 啟動

系統是通過對MetaServerBootstrap的控制來完成了啟動。

MetaServer 子產品的各個 bean 在 JavaConfig 中統一配置,JavaConfig 類為 MetaServerConfiguration。

啟動入口類為 MetaServerInitializerConfiguration,該類不由 JavaConfig 管理配置,而是繼承了 SmartLifecycle 接口,在啟動時由 Spring 架構調用其 start 方法。

具體見下圖,因為 metaServerBootstrap 是通過配置生成,是以init過程指向配置部分。

手機上如下

源碼 狀态機_[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

6.1 架構

MetaServerBootstrap是核心啟動類,該類主要包含了三類元件:外部節點通信元件、Raft 服務通信元件、定時器元件。

  • 外部節點通信元件:在該類中有幾個 Server 通信對象,用于和其它外部節點進行通信。其中 httpServer 主要提供一系列 http 接口,用于 dashboard 管理、資料查詢等;sessionServer 主要是處理一些session相關的服務;dataServer 則負責資料相關服務;metaServer 負責meta server的注冊;
  • Raft 服務 :用于叢集間的變更和資料同步,raftExchanger 就起到這個作用;
  • 定時器元件:例如定時檢測節點資訊、定時檢測資料版本資訊;具體可見 ExecutorManager,這是一個啟動各種管理線程的地方。他的啟動設定是在 MetaServerBootstrap.initRaft  之中 。

6.2 類定義

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 sessionServerHandlers;@Resource(name = "dataServerHandlers")private Collection dataServerHandlers;@Resource(name = "metaServerHandlers")private Collection metaServerHandlers;@Autowiredprivate ResourceConfig                    jerseyResourceConfig;@Autowiredprivate ApplicationContext                applicationContext;@Autowiredprivate RaftExchanger                     raftExchanger;private Server                            sessionServer;private Server                            dataServer;private Server                            metaServer;private Server                            httpServer;
}
           

可以參見下圖

手機參見下圖

源碼 狀态機_[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

6.3 通信 Exchange

因為前面代碼中有

這裡要特殊說明下Exchange。

Exchange 作為 Client / Server 連接配接的抽象,負責節點之間的連接配接。在建立連接配接中,可以設定一系列應對不同任務的 handler (稱之為 ChannelHandler),這些 ChannelHandler 有的作為 Listener 用來處理連接配接事件,有的作為 Processor 用來處理各種指定的事件,比如服務資訊資料變化、Subscriber 注冊等事件。

源碼 狀态機_[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

圖4 - 每一層各司其職,協同實作節點通信

圖 - 每一層各司其職,協同實作節點通信

各種節點在啟動的時候,利用 Exchange 設定了一系列 ChannelHandler,比如:

6.4 啟動入口

前面已經提到,啟動入口類為 MetaServerInitializerConfiguration,該類不由 JavaConfig 管理配置,而是繼承了 SmartLifecycle 接口,在啟動時由 Spring 架構調用其 start 方法。

該方法中調用了 MetaServerBootstrap # start 方法,用于啟動一系列的初始化服務。

6.4.1 啟動服務Server

前面提到,MetaServerBootstrap 在啟動時,會啟動三個 Bolt Server,并且注冊 Processor Handler,處理對應的請求:

  • DataServer:處理 DataNode 相關的請求;
  • SessionServer:處理 SessionNode 相關的請求;
  • MetaServer:處理MetaNode相關的請求;

然後啟動 HttpServer, 用于處理 Admin 請求,提供推送開關,叢集資料查詢等 Http 接口。

最後啟動 Raft 服務, 每個節點同時作為 RaftClient 和 RaftServer, 用于叢集間的變更和資料同步。為支援高可用特性,對于 MetaServer 來說,存儲了 SOFARegistry 的中繼資料,為了保障 MetaServer 叢集的一緻性,其采用了 Raft 協定來進行選舉和複制。

具體代碼參見:

6.4.1.1 BoltServer

Raft 和 Bolt 是SOFA所特殊實作,是以我們暫不介紹其底層機制,以後有機會單獨開篇,下面提一下三個BoltServer。

這幾個Server的handler就是我們前面配置的

@Resource(name = "sessionServerHandlers")
private Collection sessionServerHandlers;@Resource(name = "dataServerHandlers")private Collection dataServerHandlers;@Resource(name = "metaServerHandlers")private Collection metaServerHandlers;
           

具體參見下圖:

手機上

源碼 狀态機_[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)[源碼閱讀] 阿裡SOFA服務注冊中心MetaServer(1)

在初始化時候,大緻堆棧如下  :

以SessionServer為例,在建構過程中,調用到  BoltExchange . open。

BoltExchange中有

BoltServer中有

最後調用,會設定同步和異步的handler。

6.4.1.2 HttpServer

以使用 Jetty 的 openHttpServer 為例

啟動 HttpServer, 用于處理 Admin 請求,提供推送開關,叢集資料查詢等 Http 接口。

其堆棧如下:

: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)
           

6.4.1.3 @RaftService

如下存儲由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  
           

6.4.2 ExecutorManager

是一個啟動各種管理線程的地方,都是定期正常管理任務。

6.4.2.1 啟動

ExecutorManager 的啟動設定是在 MetaServerBootstrap.initRaft 之中,分别啟動RaftServer,RaftClient,CliService。

當  Raft 選出  Leader 之後,會調用到   ExecutorManager # startScheduler。

  • 首先生成各個ThreadPoolExecutor;
  • 然後運作本身的 各個TimedSupervisorTask,其會調用不同的handler,比如  connectServer,getSchedulerHeartbeatFirstDelay  等等;

6.4.2.2 TimedSupervisorTask

TimedSupervisorTask 實作了 TimerTask。

6.4.2.3 管理任務

可以看到管理任務大緻有:

  • HeartbeatCheck :心跳檢測;
  • GetOtherDataCenterChange :檢視其他資料中心的變化;
  • ConnectMetaServer :與其他的MetaServer互動;
  • CheckSessionNodeListChangePush :看看Session節點的變化;
  • CheckDataNodeListChangePush :檢視資料節點變化;
  • RaftClientRefresh :看看Raft 服務消息;

TimedSupervisorTask 會定期執行,比如  CheckDataNodeListChangePush  這個線程會定期執行  metaServerRegistry.pushNodeListChange(NodeType.DATA))  來看看是否有變化。這裡就會用到DataNode注冊時候,Confirm的消息。

@Override
public void pushNodeListChange() {
    NodeOperator fireNode;if ((fireNode = dataConfirmStatusService.peekConfirmNode()) != null) {
        NodeChangeResult nodeChangeResult = getNodeChangeResult();
        Map> map = nodeChangeResult.getNodes();
        Map addNodes = map.get(nodeConfig.getLocalDataCenter());if (addNodes != null) {
            Map 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 expiredNodes = storeService.getExpired();if (expiredNodes != null && !expiredNodes.isEmpty()) {
                    storeService.removeNodes(expiredNodes);
                }
            }
        }
    }  
}
           

6.4.3 ServiceFactory

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       storeServiceMap   = new HashMap<>();private static Map connectManagerMap = new HashMap<>();private static Map        nodeServiceMap    = new HashMap<>();  
}
storeServiceMap = {[email protected]}  size = 3
 {[email protected]} "SESSION" -> {[email protected]} 
  key = {[email protected]} "SESSION"
  value = {[email protected]} 
 {[email protected]} "DATA" -> {[email protected]} 
  key = {[email protected]} "DATA"
  value = {[email protected]} 
 {[email protected]} "META" -> {[email protected]} 
  key = {[email protected]} "META"
  value = {[email protected]} 
connectManagerMap = {[email protected]}  size = 3
 {Node[email protected]} "SESSION" -> {[email protected]} 
  key = {[email protected]} "SESSION"
  value = {[email protected]} 
 {[email protected]} "DATA" -> {[email protected]} 
  key = {[email protected]} "DATA"
  value = {[email protected]} 
 {[email protected]} "META" -> {[email protected]} 
  key = {[email protected]} "META"
  value = {[email protected]} 
nodeServiceMap = {[email protected]}  size = 3
 {No[email protected]} "SESSION" -> {[email protected]} 
  key = {[email protected]} "SESSION"
  value = {[email protected]} 
 {[email protected]} "DATA" -> {[email protected]} 
  key = {[email protected]} "DATA"
  value = {[email protected]} 
 {[email protected]} "META" -> {[email protected]} 
  key = {[email protected]} "META"
  value = {[email protected]} 
           

至此,MetaServer的架構和啟動介紹完成,我們下文将會介紹基本功能,比如注冊,存儲,續約等。

0xFF 參考

服務注冊中心 MetaServer 功能介紹和實作剖析 | SOFARegistry 解析

服務注冊中心如何實作 DataServer 平滑擴縮容 | SOFARegistry 解析

服務注冊中心資料一緻性方案分析 | SOFARegistry 解析

服務注冊中心如何實作秒級服務上下線通知 | SOFARegistry 解析

服務注冊中心 Session 存儲政策 | SOFARegistry 解析

服務注冊中心資料分片和同步方案詳解 | SOFARegistry 解析

服務注冊中心 SOFARegistry 解析 | 服務發現優化之路

海量資料下的注冊中心 - SOFARegistry 架構介紹

服務端部署

用戶端使用

全面了解Raft協定

詳解螞蟻金服 SOFAJRaft | 生産級高性能 Java 實作

從JRaft來看Raft協定實作細節

SOFAJRaft—初次使用

JRaft 使用者指南 & API 詳解

怎樣打造一個分布式資料庫——rocksDB, raft, mvcc,本質上是為了解決跨資料中心的複制

sofa-bolt源碼閱讀(5)-日志

Raft 為什麼是更易了解的分布式一緻性算法

SOFAJRaft 源碼分析一(啟動流程和節點變化)

SOFAJRaft 實作原理 - 生産級 Raft 算法庫存儲子產品剖析

用戶端使用