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官网有详细介绍)。服务注册的大致流程已经说清楚了。详细逻辑比较复杂。有兴趣的可以去研究源码。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐