[從原始碼學設計]螞蟻金服SOFARegistry之Data節點變更

羅西的思考發表於2020-12-26

[從原始碼學設計]螞蟻金服SOFARegistry之Data節點變更

0x00 摘要

SOFARegistry 是螞蟻金服開源的一個生產級、高時效、高可用的服務註冊中心。

本系列文章重點在於分析設計和架構,即利用多篇文章,從多個角度反推總結 DataServer 或者 SOFARegistry 的實現機制和架構思路,讓大家藉以學習阿里如何設計。

本文為第十一篇,介紹SOFARegistry如何處理Data節點變化,即處理DataServerChangeEvent訊息。

0x02 引子

上文中我們提到,MetaServerChangeEvent也會轉化為 DataServerChangeEvent,投放到EventCenter。

這是因為Meta Server的這個推送,也許是在告訴data Server,"hi,目前data server也有變動"。所以本期我們介紹如何處理DataServerChangeEvent,此處需要結合上文。

0x03 業務範疇

我們在這裡首先要講講幾個業務範疇。

3.1 DataServer 資料一致性

為支援海量資料,SOFARegistry 採用了一致性 Hash 來分片儲存 Publisher 資料,避免了單個伺服器儲存全量資料時產生的容量瓶頸問題。而在這個模型中,每個資料分片擁有多個副本,當儲存註冊數的 DataServer 進行擴容、縮容時,MetaServer 會把這個變更通知到 DataServer 和 SessionServer,資料分片會在叢集內部進行資料遷移與同步,此時就出現了 DataServer 內部資料的一致性問題。

3.2 節點變更時的資料同步

MetaServer 會通過網路連線感知到新節點上線或者下線,所有的 DataServer 中執行著一個定時重新整理連線的任務 ConnectionRefreshTask,該任務定時去輪詢 MetaServer,獲取資料節點的資訊。需要注意的是,除了 DataServer 主動去 MetaServer 拉取節點資訊外,MetaServer 也會主動傳送 NodeChangeResult 請求到各個節點,通知節點資訊發生變化,推拉獲取資訊的最終效果是一致的。

0x04 總體邏輯

這部分總體邏輯如下:

當輪詢資訊返回資料節點有變化時,會向 EventCenter 投遞一個 DataServerChangeEvent 事件,在該事件的處理器中,如果判斷出是當前機房節點資訊有變化,則會投遞新的事件 LocalDataServerChangeEvent

該事件的處理器 LocalDataServerChangeEventHandler 中會判斷當前節點是否為新加入的節點,如果是新節點則會向其它節點傳送 NotifyOnlineRequest 請求,如圖所示:

圖 DataServer 節點上線時新節點的邏輯

本文就主要講解從DataServerChangeEvent到LocalDataServerChangeEvent這部分的邏輯

0x05 DataServerChangeEvent

5.1 訊息來源

DataServerChangeEvent有三種來源:啟動主動獲取,定期,推送。這三種具體如下

  • 啟動主動獲取:這個主動查詢並且拉取的過程,這個過程基本上類似一個同步過程,體現為客戶端一次查詢結果的同步返回。
  • 版本變更推送:為了確定服務釋出資料的變更,對於這個服務感興趣的所有客戶端訂閱方都需要推送,進行推送。由於效能要求必須併發執行並且非同步確定推送成功。
  • 定期輪訓:這樣避免了某次變更通知沒有通知到所有訂閱方的情況。

因為有了上文的知識,我們應該知道,啟動主動獲取 和 推送 這兩種方式是通過MetaServerChangeEvent完成的,結合上文邏輯圖,現在簡述如下:

+-------------------------------+
|[DataServerBootstrap]          |   MetaServerChangeEvent
|                               |
|                               +-------------------------+
|       startRaftClient         |           a             |
|                               |                         |              +---------------+
|                               |                         |              |               |
+-------------------------------+                         |              |               |
+-------------------------------+                         |              |               |
| [Timer]                       |                         |              v               |
|                               |            b            |  1   +-------+-----+         |
|  ConnectionRefreshMetaTask    +------------------------------> | EventCenter +----+    |
|                               | MetaServerChangeEvent   |      +-------+-----+    |    |
+-------------------------------+                         |              ^          |    |
+-------------------------------+                         |              |          |    |
|                               |                         |              |          |    |
| [Push<NodeChangeResult>]      |                         |              |          |    |
|                               |            c            |              |          |    |
|                               +-------------------------+              |          |    |
|                               |  MetaServerChangeEvent                 |          |    |
|      ServerChangeHandler      |                               2        |          |    |
|                               +----------------------------------------+          |    |
+-------------------------------+      DataServerChangeEvent                        |    |
                                                                                    |    |
                                                                                    |    |
                                 MetaServerChangeEvent                              |    |
                                                                   3                |    |
                               +----------------------------------------------------+    |
                               |                                                         |
                               v                                                         |
             +-----------------+--------------+         DataServerChangeEvent            |
             |                                |                                   4      |
             |  MetaServerChangeEventHandler  +------------------------------------------+
             |                                |
             +--------------------------------+

5.1.1 啟動

5.1.1.1 產生訊息

當 DataServer 節點初始化成功後,會啟動任務自動去連線 MetaServer。啟動時,會從配置裡面讀取meta server配置,metaServerService.getMetaServerMap();據此構建MetaServerChangeEvent,投放到EventCenter之中。

private void startRaftClient() {
    metaServerService.startRaftClient();
    eventCenter.post(new MetaServerChangeEvent(metaServerService.getMetaServerMap()));
}
5.1.1.2 MetaServerChangeEventHandler

MetaServerChangeEventHandler 用來響應 MetaServerChangeEvent 訊息。因為其繼承了AbstractEventHandler,所以 MetaServerChangeEventHandler 已經註冊到了EventCenter之上。

在處理MetaServerChangeEvent之後,該任務會往事件中心 EventCenter 註冊一個 DataServerChangeEvent 事件,該事件註冊後會被觸發,之後將對新增節點計算 Hash 值,同時進行納管分片。

就是對應上圖 a,1,3,4這條線,是DataServerChangeEvent的來源1

5.1.2 推送

這個來源是其他訊息的轉換,即NodeChangeResult的轉換。而且有兩個轉換過程。

5.1.2.1 推送

除了 DataServer 主動去 MetaServer 拉取節點資訊外,MetaServer 也會主動傳送 NodeChangeResult 請求到各個節點,通知節點資訊發生變化,推拉獲取資訊的最終效果是一致的。

5.1.2.2 第一層轉換

ServerChangeHandler 是 metaClientHandler 的一部分,是MetaNodeExchanger 的響應函式。

在ServerChangeHandler之中,拿到了NodeChangeResult之後,會判斷變更節點型別,這裡會根據 Note 型別不同,決定產生 DataServerChangeEvent 還是 MetaServerChangeEvent。

如果是NodeType.META,就傳送訊息給eventCenter,即eventCenter.post(new MetaServerChangeEvent(map));

這就是MetaServerChangeEvent和DataServerChangeEvent來源之一就是對應上圖2這條線,是DataServerChangeEvent的來源2

public class ServerChangeHandler extends AbstractClientHandler<NodeChangeResult> {

    @Autowired
    private EventCenter         eventCenter;

    @Autowired
    private DataServerConfig    dataServerConfig;

    @Override
    public Object doHandle(Channel channel, NodeChangeResult request) {
        ExecutorFactory.getCommonExecutor().execute(() -> {
          
            if (request.getNodeType() == NodeType.DATA) {
              
                eventCenter.post(new DataServerChangeEvent(request.getNodes(),
                        request.getDataCenterListVersions(), FromType.META_NOTIFY));
              
            } else if (request.getNodeType() == NodeType.META) {
              
                Map<String, Map<String, MetaNode>> metaNodesMap = request.getNodes();
                if (metaNodesMap != null && !metaNodesMap.isEmpty()) {
                    Map<String, MetaNode> metaNodeMap = metaNodesMap.get(dataServerConfig.getLocalDataCenter());
                    if (metaNodeMap != null && !metaNodeMap.isEmpty()) {
                        HashMap<String, Set<String>> map = new HashMap<>();
                        map.put(dataServerConfig.getLocalDataCenter(), metaNodeMap.keySet());
                        eventCenter.post(new MetaServerChangeEvent(map));
                    }
                }
            }
        });
        return CommonResponse.buildSuccessResponse();
    }

    @Override
    public Class interest() {
        return NodeChangeResult.class;
    }

    @Override
    public HandlerType getType() {
        return HandlerType.PROCESSER;
    }

    @Override
    protected Node.NodeType getConnectNodeType() {
        return Node.NodeType.DATA;
    }
}
5.1.2.3 第二層轉換

MetaServerChangeEventHandler 用來響應 MetaServerChangeEvent 訊息。因為其繼承了AbstractEventHandler,所以 MetaServerChangeEventHandler 已經註冊到了EventCenter之上。

注意,這裡有一個再次轉換DataServerChangeEvent的過程,即MetaServerChangeEventHandler這裡會再主動和MetaServer互動,這是因為Meta Server的這個推送,也許是在告訴data Server,"hi,目前data server也有變動"。

如果返回訊息是NodeChangeResult,就轉換為DataServerChangeEvent,投放DataServerChangeEvent到Event Center。

就是對應上圖 b,1,3,4這條線,是DataServerChangeEvent的來源3

public class MetaServerChangeEventHandler extends AbstractEventHandler<MetaServerChangeEvent> {
    private void registerMetaServer(String dataCenter, String ip) {
        ......
                    if (obj instanceof NodeChangeResult) {
                        NodeChangeResult<DataNode> result = (NodeChangeResult<DataNode>) obj;
                        Map<String, Long> versionMap = result.getDataCenterListVersions();
    
                        //send renew after first register dataNode
                        Set<StartTaskTypeEnum> set = new HashSet<>();
                        set.add(StartTaskTypeEnum.RENEW);
                        eventCenter.post(new StartTaskEvent(set));
    
                        eventCenter.post(new DataServerChangeEvent(result.getNodes(), versionMap,
                                DataServerChangeEvent.FromType.REGISTER_META));
                        break;
                    }
    }
}

0x06 輪訓拉

我們這裡要重點講解DataServerChangeEvent的來源“輪訓拉”

MetaServer 會通過網路連線感知到新節點上線或者下線,所有的 DataServer 中執行著一個定時重新整理連線的任務 ConnectionRefreshTask,該任務定時去輪詢 MetaServer,獲取資料節點的資訊。

6.1 Bean

ConnectionRefreshTask 在 tasks 這個 Bean中啟動。

@Bean(name = "tasks")
public List<AbstractTask> tasks() {
    List<AbstractTask> list = new ArrayList<>();
    list.add(connectionRefreshTask());
    list.add(connectionRefreshMetaTask());
    list.add(renewNodeTask());
    return list;
}

6.2 啟動

tasks是在startScheduler間接啟動的。

eventCenter.post(new StartTaskEvent(
        Arrays.stream(StartTaskTypeEnum.values()).filter(type -> type != StartTaskTypeEnum.RENEW).collect(Collectors.toSet())));

StartTaskEventHandler響應StartTaskEvent,其會逐一啟動tasks。

public class StartTaskEventHandler extends AbstractEventHandler<StartTaskEvent> {
 
    @Resource(name = "tasks")
    private List<AbstractTask>       tasks;

    private ScheduledExecutorService executor     = null;

    @Override
    public void doHandle(StartTaskEvent event) {
        if (executor == null || executor.isShutdown()) {
            getExecutor();
        }

        for (AbstractTask task : tasks) {
            if (event.getSuitableTypes().contains(task.getStartTaskTypeEnum())) {
                executor.scheduleWithFixedDelay(task, task.getInitialDelay(), task.getDelay(),
                    task.getTimeUnit());
            }
        }
    }

    private void getExecutor() {
        executor = ExecutorFactory.newScheduledThreadPool(tasks.size(), this.getClass()
            .getSimpleName());
    }
}

這裡有一個技巧。

ConnectionRefreshTask裡面指定了支援CONNECT_DATA,StartTaskEventHandler在啟動時判斷支援型別,發現是CONNECT_DATA,就啟動了ConnectionRefreshTask

AbstractEventHandler 其中註冊了eventCenter.register,這樣它的繼承類都預設註冊到了EventCenter 之上。

public abstract class AbstractEventHandler<Event> implements InitializingBean {
    @Autowired
    private EventCenter         eventCenter;

    @Override
    public void afterPropertiesSet() throws Exception {
        eventCenter.register(this);
    }

    /**
     * event handle func
     * @param event
     */
    public void handle(Event event) {
            doHandle(event);
    }

    public abstract List<Class<? extends Event>> interest();

    public abstract void doHandle(Event event);
}

於是,connectionRefreshTask就啟動了。

6.3 ConnectionRefreshTask

ConnectionRefreshTask負責輪詢與meta Server互動,可以看到,也傳送了DataServerChangeEvent。

public class ConnectionRefreshTask extends AbstractTask {

    @Autowired
    private IMetaServerService metaServerService;

    @Autowired
    private EventCenter        eventCenter;

    @Override
    public void handle() {
        DataServerChangeItem dataServerChangeItem = metaServerService.getDateServers();
        if (dataServerChangeItem != null) {
            eventCenter
                .post(new DataServerChangeEvent(dataServerChangeItem, FromType.CONNECT_TASK));
        }
    }

    @Override
    public int getDelay() {
        return 30;
    }

    @Override
    public int getInitialDelay() {
        return 0;
    }

    @Override
    public TimeUnit getTimeUnit() {
        return TimeUnit.SECONDS;
    }

    @Override
    public StartTaskTypeEnum getStartTaskTypeEnum() {
        return StartTaskTypeEnum.CONNECT_DATA;
    }
}

ConnectionRefreshTask 呼叫 metaServerService.getDateServers();getDateServers 的作用是:

  • 從 metaServerConnectionFactory 獲取connectionMap;
  • 通過raft來獲取raft leader;
  • 從 connectionMap 獲取 leader 的connection,這是一個 bolt Connection;
  • 利用 bolt Connection 進行請求,GetNodesRequest(NodeType.DATA);
  • 從請求結果構建 DataServerChangeItem;
  • 在 EventCenter 中放一個訊息 DataServerChangeEvent(dataServerChangeItem, FromType.CONNECT_TASK);

具體如下:

@Override
public DataServerChangeItem getDateServers() {
    Map<String, Connection> connectionMap = metaServerConnectionFactory
        .getConnections(dataServerConfig.getLocalDataCenter());
    String leader = getLeader().getIp();
    if (connectionMap.containsKey(leader)) {
        Connection connection = connectionMap.get(leader);
        if (connection.isFine()) {
            try {
                GetNodesRequest request = new GetNodesRequest(NodeType.DATA);
                Object obj = metaNodeExchanger.request(new Request() {
                    @Override
                    public Object getRequestBody() {
                        return request;
                    }

                    @Override
                    public URL getRequestUrl() {
                        return new URL(connection.getRemoteIP(), connection.getRemotePort());
                    }
                }).getResult();
                if (obj instanceof NodeChangeResult) {
                    NodeChangeResult<DataNode> result = (NodeChangeResult<DataNode>) obj;
                    Map<String, Long> versionMap = result.getDataCenterListVersions();
                    versionMap.put(result.getLocalDataCenter(), result.getVersion());
                    return new DataServerChangeItem(result.getNodes(), versionMap);
                }
            } 
        }
    }
    String newip = refreshLeader().getIp();
    return null;
}

0x07 DataServerChangeEventHandler

7.1 總體邏輯

DataServerChangeEvent 事件被觸發後,由 DataServerChangeEventHandler 來進行相應的處理,分別分為如下一些步驟:

  • 初始化當前資料節點的一致性 Hash 值,把當前節點新增進一致性的 Hash 環中。
  • 獲取變更了的 DataServer 節點,這些節點在啟動 DataServer 服務的時候從 MetaServer 中獲取到的,並且通過 DataServerChangeEvent 事件中的 DataServerChangeItem 傳入。
  • 獲取了當前的 DataServer 節點之後,若節點列表非空,則遍歷每個節點,建立當前節點與其餘資料節點之間的連線,同時刪除本地維護的不在節點列表中的節點資料,更新dataServerCache。同時,若當前節點是 DataCenter 節點,則觸發 LocalDataServerChangeEvent 事件

SOFA這裡主要是處理LocalDataServerChangeEvent,異地機房的部分沒有開源

7.2 LocalDataServerChangeEvent

關於上面第三點,詳細說明如下:

從DataServerChangeEvent中提取DataServerChangeItem,如果發現有一個DataCenter就是本機,則使用如下語句獲取新加入的DataServer。

Set<String> newjoined = new HashSet<>(ips);
newjoined.removeAll(localDataServers);

然後使用這些新加入的DataServer來構建 LocalDataServerChangeEvent。

參見如下片段:

//get changed dataservers
Map<String, Set<String>> changedMap = dataServerCache.compareAndSet(
                dataServerChangeItem, event.getFromType());
if (!changedMap.isEmpty()) {
for (Entry<String, Set<String>> changeEntry : changedMap.entrySet()) {
                    String dataCenter = changeEntry.getKey();
                    Set<String> ips = changeEntry.getValue();
                  
		String dataCenter = changeEntry.getKey();
    Set<String> ips = changeEntry.getValue();

    //if the dataCenter is self, post LocalDataServerChangeEvent
    if (dataServerConfig.isLocalDataCenter(dataCenter)) {
        Set<String> newjoined = new HashSet<>(ips);
        newjoined.removeAll(localDataServers);
        eventCenter.post(new LocalDataServerChangeEvent(map, newjoined,
            dataServerChangeItem.getVersionMap().get(dataCenter), newVersion));
    } else {
        dataServerCache.updateItem(newDataNodes, newVersion, dataCenter);
        eventCenter.post(new RemoteDataServerChangeEvent(dataCenter, map,
            dataServerChangeItem.getVersionMap().get(dataCenter), newVersion));
    }
}

具體程式碼如下:

public class DataServerChangeEventHandler extends AbstractEventHandler<DataServerChangeEvent> {

    private static final int    TRY_COUNT = 5;

    @Autowired
    private DataServerConfig    dataServerConfig;

    @Autowired
    private DataServerCache     dataServerCache;

    @Autowired
    private DataNodeExchanger   dataNodeExchanger;

    @Autowired
    private EventCenter         eventCenter;

    @Override
    public List<Class<? extends DataServerChangeEvent>> interest() {
        return Lists.newArrayList(DataServerChangeEvent.class);
    }

    @Override
    public void doHandle(DataServerChangeEvent event) {
        synchronized (this) {
            //register self first,execute once
            DataServerNodeFactory.initConsistent(dataServerConfig);

            DataServerChangeItem dataServerChangeItem = event.getDataServerChangeItem();
            Set<String> localDataServers = dataServerCache.getDataServers(
                dataServerConfig.getLocalDataCenter()).keySet();
          
            //get changed dataservers 得到變化了的dataServers
            Map<String, Set<String>> changedMap = dataServerCache.compareAndSet(
                dataServerChangeItem, event.getFromType());
          
            if (!changedMap.isEmpty()) {
                for (Entry<String, Set<String>> changeEntry : changedMap.entrySet()) {
                    String dataCenter = changeEntry.getKey();
                    Set<String> ips = changeEntry.getValue();
                    Long newVersion = dataServerCache.getDataCenterNewVersion(dataCenter);
                    if (!CollectionUtils.isEmpty(ips)) {
                        for (String ip : ips) {
                            if (!StringUtils.equals(ip, DataServerConfig.IP)) {
                                DataServerNode dataServerNode = DataServerNodeFactory
                                    .getDataServerNode(dataCenter, ip);
                                if (dataServerNode == null
                                    || dataServerNode.getConnection() == null
                                    || !dataServerNode.getConnection().isFine()) {
                                    connectDataServer(dataCenter, ip);
                                }
                            }
                        }
                        //remove all old DataServerNode not in change map
                        Set<String> ipSet = DataServerNodeFactory.getIps(dataCenter);
                        for (String ip : ipSet) {
                            if (!ips.contains(ip)) {
                                DataServerNodeFactory.remove(dataCenter, ip, dataServerConfig);
                            }
                        }

                        Map<String, DataNode> newDataNodes = dataServerCache
                            .getNewDataServerMap(dataCenter);

                        //avoid input map reference operation DataServerNodeFactory MAP
                        Map<String, DataNode> map = new ConcurrentHashMap<>(newDataNodes);

                        //if the dataCenter is self, post LocalDataServerChangeEvent
                        if (dataServerConfig.isLocalDataCenter(dataCenter)) {
                          //為什麼 local 時候不做 updateItem
                            Set<String> newjoined = new HashSet<>(ips);
                            newjoined.removeAll(localDataServers);
                            eventCenter.post(new LocalDataServerChangeEvent(map, newjoined,
                                dataServerChangeItem.getVersionMap().get(dataCenter), newVersion));
                        } else {
                            dataServerCache.updateItem(newDataNodes, newVersion, dataCenter);
                            eventCenter.post(new RemoteDataServerChangeEvent(dataCenter, map,
                                dataServerChangeItem.getVersionMap().get(dataCenter), newVersion));
                        }
                    } else {
                        //if the dataCenter which has no dataServers is not self, remove it
                        if (!dataServerConfig.isLocalDataCenter(dataCenter)) {
                            removeDataCenter(dataCenter);
                            eventCenter.post(new RemoteDataServerChangeEvent(dataCenter,
                                Collections.EMPTY_MAP, dataServerChangeItem.getVersionMap().get(
                                    dataCenter), newVersion));
                        }
                        Map<String, DataNode> newDataNodes = dataServerCache
                            .getNewDataServerMap(dataCenter);
                        dataServerCache.updateItem(newDataNodes, newVersion, dataCenter);
                    }
                }
            } else {
                //refresh for keep connect other dataServers
                //如果沒有“有變化”的DataServer,則重新連線一下現有的DataServer
                Set<String> allDataCenter = new HashSet<>(dataServerCache.getAllDataCenters());
                for (String dataCenter : allDataCenter) {
                    Map<String, DataNode> dataNodes = dataServerCache
                        .getNewDataServerMap(dataCenter);
                    if (dataNodes != null) {
                        for (DataNode dataNode : dataNodes.values()) {
                            if (!StringUtils.equals(dataNode.getIp(), DataServerConfig.IP)) {
                                DataServerNode dataServerNode = DataServerNodeFactory
                                    .getDataServerNode(dataCenter, dataNode.getIp());
                                Connection connection = dataServerNode != null ? dataServerNode
                                    .getConnection() : null;
                                if (connection == null || !connection.isFine()) {
                                    connectDataServer(dataCenter, dataNode.getIp());
                                }
                            }
                        }
                    }
                }
            }
        }
    }
  
    /**
     * connect specific dataserver
     *
     * @param dataCenter
     * @param ip
     */
    private void connectDataServer(String dataCenter, String ip) {
        Connection conn = null;
        for (int tryCount = 0; tryCount < TRY_COUNT; tryCount++) {
            try {
                conn = ((BoltChannel) dataNodeExchanger.connect(new URL(ip, dataServerConfig
                    .getSyncDataPort()))).getConnection();
                break;
            } catch (Exception e) {
                TimeUtil.randomDelay(3000);
            }
        }
        if (conn == null || !conn.isFine()) {
            throw new RuntimeException(
                String
                    .format(
                        "[DataServerChangeEventHandler] connect dataServer %s in %s failed five times,dataServer will not work,please check connect!",
                        ip, dataCenter));
        }
        //maybe get dataNode from metaServer,current has not start! register dataNode info to factory,wait for connect task next execute
        DataServerNodeFactory.register(new DataServerNode(ip, dataCenter, conn), dataServerConfig);
    }

    /**
     * remove dataCenter, and close connections of dataServers in this dataCenter
     *
     * @param dataCenter
     */
    private void removeDataCenter(String dataCenter) {
        DataServerNodeFactory.getDataServerNodes(dataCenter).values().stream().map(DataServerNode::getConnection)
                .filter(connection -> connection != null && connection.isFine()).forEach(Connection::close);
        DataServerNodeFactory.remove(dataCenter);
    }
}

7.3 邏輯圖

於是,我們的邏輯圖擴充如下:

DataServerChangeEvent一共四個來源。

前三個來源是與MetaServerChangeEvent相關。

  • 啟動主動獲取:MetaServerChangeEventHandler 用來響應 MetaServerChangeEvent 訊息。在處理MetaServerChangeEvent之後,該任務會往事件中心 EventCenter 註冊一個 DataServerChangeEvent 事件就是對應下圖 a,1,3,4這條線,是DataServerChangeEvent的來源1
  • 版本變更推送:ServerChangeHandler 是 MetaNodeExchanger 的響應函式。在ServerChangeHandler之中,拿到了NodeChangeResult之後,會判斷變更節點型別,這裡會根據 Note 型別不同,決定產生 DataServerChangeEvent 還是 MetaServerChangeEvent。
    • 如果是NodeType.DATA,ServerChangeHandler 就傳送訊息給eventCenter,即eventCenter.post(new MetaServerChangeEvent(map));就是對應下圖2這條線,是DataServerChangeEvent的來源2
    • 如果是NodeType.DATA,ServerChangeHandler 就傳送訊息給eventCenter,即eventCenter.post(new MetaServerChangeEvent(map));;注意,這裡有一個再次轉換DataServerChangeEvent的過程,即MetaServerChangeEventHandler這裡會再主動和MetaServer互動,如果返回訊息是NodeChangeResult,就轉換為DataServerChangeEvent,投放DataServerChangeEvent到Event Center。就是對應下圖 b,1,3,4這條線,是DataServerChangeEvent的來源3

第四來源是定期輪訓。

所有的 DataServer 中執行著一個定時重新整理連線的任務 ConnectionRefreshTask,該任務定時去輪詢 MetaServer,獲取資料節點的資訊。

ConnectionRefreshTask 呼叫 metaServerService.getDateServers();與MetaServer聯絡,從請求結果構建 DataServerChangeItem;在 EventCenter 中放一個訊息 DataServerChangeEvent(dataServerChangeItem, FromType.CONNECT_TASK);就是對應下圖5這條線,是DataServerChangeEvent的來源3

最後,DataServerChangeEvent 事件被觸發後,由 DataServerChangeEventHandler 來進行相應的處理。就是對應下圖6,7這條線

若當前節點是 DataCenter 節點,則觸發 LocalDataServerChangeEvent 事件。SOFA這裡主要是處理LocalDataServerChangeEvent,異地機房的部分沒有開源

+---------------------------+
|[DataServerBootstrap]      |   MetaServerChangeEvent         +------------------------+
|                           |                                 |                        |
|                           +-------------------------+       |   +------------------+ |
|       startRaftClient     |           a             |       |   |                  | |
|                           |                         |       |   |  +-------------+ | |
|                           |                         |       |   |  |             | | |
+---------------------------+                         |       |   |  |             | | |
+---------------------------+                         |       v   |  |             | | |
| [Timer]                   |                         |           |  v             | | |
|                           |           b             |  1   +----+--+-----+       | | |
| ConnectionRefreshMetaTask +------------------------------> | EventCenter +----+  | | |
|                           | MetaServerChangeEvent   |      +-------+---+-+    |  | | |
+---------------------------+                         |              ^   ^      |  | | |
+---------------------------+                         |              |   |      |  | | |
|                           |                         |              |   |      |  | | |
| [Push<NodeChangeResult>]  |                         |              |   |      |  | | |
|                           |           c             |              |   |      |  | | |
|                           +-------------------------+              |   | 5    |  | | |
|                           |  MetaServerChangeEvent                 |   |      |  | | |
|      ServerChangeHandler  |                               2        |   |      |  | | |
|                           +----------------------------------------+   |      |  | | |
+---------------------------+      DataServerChangeEvent                 |      |  | | |
                                                                         |      |  | | |
+-------------------------+                                              |      |  | | |
|                         |                                              |      |  | | |
| ConnectionRefreshTask   +----------------------------------------------+      |  | | |
|                         |                                                     |  | | |
+-------------------------+                                                     |  | | |
                                                                                |  | | |
                             MetaServerChangeEvent                              |  | | |
                                                               3                |  | | |
                           +----------------------------------------------------+  | | |
                           |                                                       | | |
                           v                                                       | | |
         +-----------------+--------------+         DataServerChangeEvent          | | |
         |                                |                                   4    | | |
         |  MetaServerChangeEventHandler  +----------------------------------------+ | |
         |                                |                                          | |
         +--------------------------------+                                          | |
                                                                                     | |
                                                   DataServerChangeEvent             | |
          +------------------------------+                                           | |
          | DataServerChangeEventHandler |  <----------------------------------------+ |
          +---------------+--------------+              6                              |
                          |                                                            |
                          |     7                                                      |
                          +------------------------------------------------------------+
                                LocalDataServerChangeEvent / RemoteDataServerChangeEvent

0x08 總結

本文講解了SOFARegistry如何處理Data節點變化。

主要就是從DataServerChangeEvent到LocalDataServerChangeEvent這部分的邏輯。SOFA這裡主要是處理LocalDataServerChangeEvent,異地機房的部分沒有開源。所以下文我們介紹LocalDataServerChangeEvent。

0xFF 參考

螞蟻金服服務註冊中心如何實現 DataServer 平滑擴縮容

螞蟻金服服務註冊中心 SOFARegistry 解析 | 服務發現優化之路

服務註冊中心 Session 儲存策略 | SOFARegistry 解析

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

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

螞蟻金服開源通訊框架SOFABolt解析之連線管理剖析

螞蟻金服開源通訊框架SOFABolt解析之超時控制機制及心跳機制

螞蟻金服開源通訊框架 SOFABolt 協議框架解析

螞蟻金服服務註冊中心資料一致性方案分析 | SOFARegistry 解析

相關文章