Dubbo使用nacos作為註冊中心原理剖析

峽谷程式猿發表於2020-07-23

Nacos是阿里自研的,一個更易於構建雲原生應用的動態服務發現、配置管理和服務管理平臺 。作為微服務註冊中心,它的目標是淘汰目前流行的eureka,zookeeper,consul等元件。現在學習它真的很有必要。這裡就還是從原始碼的角度出發,看一下Spring Boot是如何和它整合的。

詳細教程官網已經寫的非常明確,這裡就不多說了。
Nacos中文官方文件
下載nacos

專案版本:

元件版本
spring-boot2.3.1.RELEASE
dubbo2.7.7.RELEASE
nacos-server1.3.0

下載好nacos-server執行包解壓後啟動(官方有詳細文件),這裡就不詳細說明了。
它的預設埠號是8848 ,這是珠峰的高度,這也表明nacos的志向,要達到珠峰的高度,超高其他同類的產品

Nacos官方文件
架構圖:
在這裡插入圖片描述

nacos既可以作為服務註冊中心 (Service Registry)。也可以作為配置中心(Configuration Management),本文只討論作為註冊中心的實現原理。

既然作為註冊中心,當然有必不可少的兩個角色,service provider 和service consumer 。它們的主要的工作流程如下:
在這裡插入圖片描述
其實目前的微服務架構都是這樣的工作,dubbo也是如此。

dubbo使用nacos作為註冊中心,需要在原有dubbo專案基礎上新增如下依賴:

<dependency>
   <groupId>org.apache.dubbo</groupId>
   <artifactId>dubbo-registry-nacos</artifactId>
   <version>${dubbo.version}</version>
</dependency>

然後將註冊中心配置改為如下:

## 其他屬性保持不變

## Nacos registry address
dubbo.registry.address = nacos://10.20.153.10:8848
##如果要使用自己建立的名稱空間可以使用下面2種方式
#dubbo.registry.address = nacos://10.20.153.10:8848?namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932
#dubbo.registry.parameters.namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932
...

這裡推薦註冊時新增nacos的名稱空間

這樣配置後,重啟服務,就能讓dubbo的provider和consumer的資訊都註冊到nacos對應的名稱空間下。
如下所示:
在這裡插入圖片描述
dubbo是支援多種註冊中心的。對於註冊中心。dubbo也同樣提供了一個SPI介面:

org.apache.dubbo.registry.RegistryFactory

下面來看RegistryFactory :

@SPI("dubbo")
public interface RegistryFactory {

    /**
     * Connect to the registry
     * <p>
     * Connecting the registry needs to support the contract: <br>
     * 1. When the check=false is set, the connection is not checked, otherwise the exception is thrown when disconnection <br>
     * 2. Support username:password authority authentication on URL.<br>
     * 3. Support the backup=10.20.153.10 candidate registry cluster address.<br>
     * 4. Support file=registry.cache local disk file cache.<br>
     * 5. Support the timeout=1000 request timeout setting.<br>
     * 6. Support session=60000 session timeout or expiration settings.<br>
     *
     * @param url Registry address, is not allowed to be empty
     * @return Registry reference, never return empty value
     */
    @Adaptive({"protocol"})
    Registry getRegistry(URL url);

}

RegistryFactory是一個SPI介面,並且提供一個Adaptive介面。用於獲取Registry例項。@SPI中指定了預設實現名是dubbo ,且getRegistry方法對於的自適應實現是protocol .用於建立一個Registry例項。

但是在使用nacos作為註冊中心時。會將nacos作為RegistryFactory的預設實現。因此需要從SPI配置檔案中去找nacos對應的RegistryFactory實現。

META-INF\dubbo\internal\org.apache.dubbo.registry.RegistryFactory配置檔案:

service-discovery-registry=org.apache.dubbo.registry.client.ServiceDiscoveryRegistryFactory
wrapper=org.apache.dubbo.registry.RegistryFactoryWrapper
dubbo=org.apache.dubbo.registry.dubbo.DubboRegistryFactory
multicast=org.apache.dubbo.registry.multicast.MulticastRegistryFactory
zookeeper=org.apache.dubbo.registry.zookeeper.ZookeeperRegistryFactory
redis=org.apache.dubbo.registry.redis.RedisRegistryFactory
consul=org.apache.dubbo.registry.consul.ConsulRegistryFactory

etcd3=org.apache.dubbo.registry.etcd.EtcdRegistryFactory
nacos=org.apache.dubbo.registry.nacos.NacosRegistryFactory
sofa=org.apache.dubbo.registry.sofa.SofaRegistryFactory
multiple=org.apache.dubbo.registry.multiple.MultipleRegistryFactory

因此可以看到。org.apache.dubbo.registry.nacos.NacosRegistryFactory將作為RegistryFactory的實現。來看其原始碼:
在這裡插入圖片描述

public class NacosRegistryFactory extends AbstractRegistryFactory {

    @Override
    protected String createRegistryCacheKey(URL url) {
        return url.toFullString();
    }

    @Override
    protected Registry createRegistry(URL url) {
        return new NacosRegistry(url, createNamingService(url));
    }
}

從這裡引出核心類NacosRegistry ,結構圖如下:

在這裡插入圖片描述

頂層介面是dubbo的RegistryService,它定義了服務的註冊到發現的5個基本介面:

public interface RegistryService { // Registry extends RegistryService 
    /**
     * 註冊服務.
     * 
     * 註冊需處理契約:<br>
     * 1. 當URL設定了check=false時,註冊失敗後不報錯,在後臺定時重試,否則丟擲異常。<br>
     * 2. 當URL設定了dynamic=false引數,則需持久儲存,否則,當註冊者出現斷電等情況異常退出時,需自動刪除。<br>
     * 3. 當URL設定了category=overrides時,表示分類儲存,預設類別為providers,可按分類部分通知資料。<br>
     * 4. 當註冊中心重啟,網路抖動,不能丟失資料,包括斷線自動刪除資料。<br>
     * 5. 允許URI相同但引數不同的URL並存,不能覆蓋。<br>
     * 
     * @param url 註冊資訊,不允許為空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
     */
    void register(URL url);
 
    /**
     * 取消註冊服務.
     * 
     * 取消註冊需處理契約:<br>
     * 1. 如果是dynamic=false的持久儲存資料,找不到註冊資料,則拋IllegalStateException,否則忽略。<br>
     * 2. 按全URL匹配取消註冊。<br>
     * 
     * @param url 註冊資訊,不允許為空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
     */
    void unregister(URL url);
 
    /**
     * 訂閱服務.
     * 
     * 訂閱需處理契約:<br>
     * 1. 當URL設定了check=false時,訂閱失敗後不報錯,在後臺定時重試。<br>
     * 2. 當URL設定了category=overrides,只通知指定分類的資料,多個分類用逗號分隔,並允許星號通配,表示訂閱所有分類資料。<br>
     * 3. 允許以interface,group,version,classifier作為條件查詢,如:interface=com.alibaba.foo.BarService&version=1.0.0<br>
     * 4. 並且查詢條件允許星號通配,訂閱所有介面的所有分組的所有版本,或:interface=*&group=*&version=*&classifier=*<br>
     * 5. 當註冊中心重啟,網路抖動,需自動恢復訂閱請求。<br>
     * 6. 允許URI相同但引數不同的URL並存,不能覆蓋。<br>
     * 7. 必須阻塞訂閱過程,等第一次通知完後再返回。<br>
     * 
     * @param url 訂閱條件,不允許為空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
     * @param listener 變更事件監聽器,不允許為空
     */
    void subscribe(URL url, NotifyListener listener);
 
    /**
     * 取消訂閱服務.
     * 
     * 取消訂閱需處理契約:<br>
     * 1. 如果沒有訂閱,直接忽略。<br>
     * 2. 按全URL匹配取消訂閱。<br>
     * 
     * @param url 訂閱條件,不允許為空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
     * @param listener 變更事件監聽器,不允許為空
     */
    void unsubscribe(URL url, NotifyListener listener);
 
    /**
     * 查詢註冊列表,與訂閱的推模式相對應,這裡為拉模式,只返回一次結果。
     * 
     * @see org.apache.dubbo.registry.NotifyListener#notify(List)
     * @param url 查詢條件,不允許為空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
     * @return 已註冊資訊列表,可能為空,含義同{@link org.apache.dubbo.registry.NotifyListener#notify(List<URL>)}的引數。
     */
    List<URL> lookup(URL url);
 
}

然後,構造NacosRegistryService時需要兩個必須引數:
① dubbo中的URL物件
URL在我往期的dubbo文章中有介紹,這裡就不在
② nacos中的名字服務 (Naming Service)
NacosNamingService是預設實現。它提供分散式系統中所有物件(Object)、實體(Entity)的“名字”到關聯的後設資料之間的對映管理服務。
例如 ServiceName -> Endpoints Info, Distributed Lock Name -> Lock Owner/Status Info, DNS Domain Name -> IP List, 服務發現和 DNS 就是名字服務的2大場景。

NacosRegistryService相當於一個介面卡。dubbo服務的註冊與發現的具體實現都是使用NacosNamingService來實現。

具體實現邏輯涉及到了ScheduledExecutorService,HashedWheelTimer,以及一些nacos對外的API介面操作(nacos官網有詳細介紹)。服務註冊的大致流程已經說清楚了。詳細邏輯比較複雜。有興趣的可以去研究原始碼。

相關文章