03篇 Nacos Client服務發現源碼分析

學習不用那麼功利,二師兄帶你從更高維度輕鬆閱讀源碼~數組

本篇帶你們經過源碼層面分析一下Nacos Client的服務發現的路程,事實可能並不像你想象的那樣簡單。緩存

Nacos服務發現

直觀的看,Nacos客戶端的服務發現,就是封裝參數、調用服務端接口、得到返回實例列表。服務器

naocos

但細化這個流程,會發現不只包括了經過NamingService獲取服務列表,在獲取服務列表的過程當中還涉及到通訊協議(Http or gRPC)、訂閱流程、故障轉移邏輯等。下面咱們根據服務發現來捋一下相關的流程。微信

先說入口程序,依舊是在NamingTest中能夠看到:markdown

NamingService namingService = NacosFactory.createNamingService(properties);
namingService.registerInstance("nacos.test.1", instance);

ThreadUtils.sleep(5000L);
// 獲取實例列表
List<Instance> list = namingService.getAllInstances("nacos.test.1");
複製代碼

關於NamingService的實例化和基本功能,在服務註冊時已經講過,這裏直接看獲取實例列表方法getAllInstances。該方法的參數就是服務的名稱。併發

通過一些列的重載方法調用,真正處理核心邏輯的方法以下:ide

@Override
public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters,
        boolean subscribe) throws NacosException {
    ServiceInfo serviceInfo;
    String clusterString = StringUtils.join(clusters, ",");
    // 是否訂閱模式
    if (subscribe) {
        // 先從客戶端緩存獲取服務信息
        serviceInfo = serviceInfoHolder.getServiceInfo(serviceName, groupName, clusterString);
        if (null == serviceInfo) {
            // 若是本地緩存不存在服務信息,則進行訂閱
            serviceInfo = clientProxy.subscribe(serviceName, groupName, clusterString);
        }
    } else {
        // 若是未訂閱服務信息,則直接從服務器進行查詢
        serviceInfo = clientProxy.queryInstancesOfService(serviceName, groupName, clusterString, 0, false);
    }
    // 從服務信息中獲去實例列表
    List<Instance> list;
    if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) {
        return new ArrayList<Instance>();
    }
    return list;
}
複製代碼

首先看重載的getAllInstances方法,比入口方法多了幾個參數,這裏不只有服務名稱,還有分組名稱(groupName)、集羣列表(clusters)、是否訂閱(subscribe)。學習

重載方法中的其餘參數已經設置了默認值。好比,分組名稱默認爲「DEFAULT_GROUP」、集羣列表默認爲空數組、是否訂閱默認爲「訂閱」。spa

上述方法整理成流程圖以下:代理

naocos

上述流程的基本邏輯爲:

若是是訂閱模式,則直接從本地緩存獲取服務信息(ServiceInfo),而後從中獲取實例列表,這是由於訂閱機制會自動同步服務器實例的變化到本地。若是本地緩存中沒有,那說明是首次調用,則進行訂閱,在訂閱完成後會得到到服務信息。

若是是非訂閱模式,那就直接請求服務器端,得到服務信息。

訂閱處理流程

在上述流程中,涉及到了訂閱邏輯,入口代碼爲獲取實例列表中的以下方法:

serviceInfo = clientProxy.subscribe(serviceName, groupName, clusterString);
複製代碼

下面就來看看該方法內部是如何進行處理的。首先,這裏的clientProxy是NamingClientProxy類的對象。對應的subscribe實現以下:

@Override
public ServiceInfo subscribe(String serviceName, String groupName, String clusters) throws NacosException {
    String serviceNameWithGroup = NamingUtils.getGroupedName(serviceName, groupName);
    String serviceKey = ServiceInfo.getKey(serviceNameWithGroup, clusters);
    // 獲取緩存中的ServiceInfo
    ServiceInfo result = serviceInfoHolder.getServiceInfoMap().get(serviceKey);
    if (null == result) {
        // 若是爲null,則進行訂閱邏輯處理,基於gRPC協議
        result = grpcClientProxy.subscribe(serviceName, groupName, clusters);
    }
    // 定時調度UpdateTask
    serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, groupName, clusters);
    // ServiceInfo本地緩存處理
    serviceInfoHolder.processServiceInfo(result);
    return result;
}
複製代碼

在上述代碼中,能夠看到在獲取服務實例列表時(特別是首次),也進行了訂閱邏輯的拓展,基本流程圖以下:

naocos

上圖流程中能夠看出,訂閱方法先經過代理類進行了本地緩存的判斷,若是本地緩存存在ServiceInfo信息,則直接返回。若是不存在,則默認採用gRPC協議進行訂閱,並返回ServiceInfo。

grpcClientProxy的subscribe訂閱方法就是直接向服務器發送了一個訂閱請求,並返回結果,就沒有作過多處理了。

訂閱完成以後,會經過ServiceInfoUpdateService開啓一個定時任務,這個定時任務主要的做用就是來定時同步服務器端的實例列表信息,並進行本地緩存更新等操做。

最後一步,ServiceInfo本地緩存處理。這裏會將得到的最新ServiceInfo與本地內存中的ServiceInfo進行比較,更新,發佈變動時間,磁盤文件存儲等操做。其實,這一步的操做,在訂閱定時任務中也進行了處理。

關於訂閱細節和本地緩存處理,涉及內容較多,咱們後面單獨拓展開講解。這裏知道總體流程便可。

小結

本文主要梳理了Nacos客戶端服務發現的核心流程,包括:

第一,若是沒有開啓訂閱模式,則直接經過/instance/list接口(默認經過gRPC協議)獲取服務實例列表信息;

第二,若是開啓訂閱模式(默認開啓),則先會從本地緩存中獲取實例信息,若是不存在,則進行訂閱獲並獲取實例信息;

第三,在開啓訂閱時,會開啓定時任務,定時執行UpdateTask(獲取服務器實例信息、更新本地緩存、發佈事件);

第四,在第二步得到最新的實例信息以後,也會執行processServiceInfo方法來更新內存和本地實例緩存,併發布變動時間。

第五,至此,與第二步造成循環,每次獲取本地緩存,不存在則更新……

關於用來處理訂閱相關的UpdateTask和用來處理本地緩存的ServiceInfoHolder#processServiceInfo方法,咱們後面文章繼續講解。

博主簡介:《SpringBoot技術內幕》技術圖書做者,酷愛鑽研技術,寫技術乾貨文章。

公衆號:「程序新視界」,博主的公衆號,歡迎關注~

技術交流:請聯繫博主微信號:zhuan2quan

相關文章
相關標籤/搜索