vlambda博客
学习文章列表

Nacos(1)——微服务下的服务注册

我们在使用Eureka时,通常会使用如下注解:

1@EnableDiscoveryClient
2@EnableEurekaClient

@EnableEurekaClient是Netfix单独为Eureka的Client端开发的注解,位于spring-cloud-netflix-eureka-client包中

@EnableDiscoveryClient是Spring Cloud的一个原生注解,位于spring-cloud-commons包中,可以视为一种规范。

接下来我们将分析Spring Cloud Alibaba套件是如何进行服务发现的。

Spring Cloud Alibaba将在本篇文章中以SCA代替

场景搭建

启动器

1@EnableDiscoveryClient
2@SpringBootApplication
3public class NacosClientThreeBoostrap {
4
5    public static void main(String[] args) {
6        SpringApplication.run(NacosClientThreeBoostrap.class);
7    }
8}

application.yml:

 1server:
2  port: 40010
3spring:
4  application:
5    name: sunshine-taurus
6  cloud:
7    nacos:
8      discovery:
9        server-addr: 127.0.0.1:8848
10management:
11  endpoints:
12    web:
13      exposure:
14        include: \*

maven依赖:

 1<dependencies>
2    <dependency>
3        <groupId>org.springframework.boot</groupId>
4        <artifactId>spring-boot-starter-web</artifactId>
5    </dependency>
6    <dependency>
7        <groupId>org.springframework.cloud</groupId>
8        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
9    </dependency>
10    <dependency>
11        <groupId>org.springframework.boot</groupId>
12        <artifactId>spring-boot-starter-actuator</artifactId>
13    </dependency>
14</dependencies>

@EnableDiscoveryClient注解去向分析

SCA对于这个注解执行的非常的简单粗暴:

1org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
2org.springframework.cloud.alibaba.nacos.NacosDiscoveryClientAutoConfiguration

直接指到了核心实现类:

 1@Configuration
2@ConditionalOnMissingBean(DiscoveryClient.class)
3@ConditionalOnNacosDiscoveryEnabled
4@EnableConfigurationProperties
5public class NacosDiscoveryClientAutoConfiguration {
6
7    @Bean
8    public DiscoveryClient nacosDiscoveryClient() {
9        // 实例化NacosDiscoveryClient
10        return new NacosDiscoveryClient();
11    }
12
13    @Bean
14    @ConditionalOnMissingBean
15    public NacosDiscoveryProperties nacosProperties() {
16        // 实例化NacosDiscoveryProperties
17        return new NacosDiscoveryProperties();
18    }
19}

链路解释:

  1. 实例化NacosDiscoveryClient,也就是实例化一个我们上报到Nacos服务发现的客户端实例。

  2. 实例化NacosDiscoveryClient属性Bean,实例化NacosDiscoveryClient会需要。

Bean: NacosDiscoveryProperties

实例化的核心:

 1public void init() throws SocketException {
2
3    serverAddr = Objects.toString(serverAddr, "");
4    endpoint = Objects.toString(endpoint, "");
5    namespace = Objects.toString(namespace, "");
6    logName = Objects.toString(logName, "");
7
8    if (StringUtils.isEmpty(ip)) {
9        // 探测本机的IP地址
10        if (StringUtils.isEmpty(networkInterface)) {
11            ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
12        }
13        else {
14            NetworkInterface netInterface = NetworkInterface
15                    .getByName(networkInterface);
16            if (null == networkInterface) {
17                throw new IllegalArgumentException(
18                        "no such interface " + networkInterface);
19            }
20
21            Enumeration<InetAddress> inetAddress = netInterface.getInetAddresses();
22            while (inetAddress.hasMoreElements()) {
23                InetAddress currentAddress = inetAddress.nextElement();
24                if (currentAddress instanceof Inet4Address
25                        && !currentAddress.isLoopbackAddress()) {
26                    ip = currentAddress.getHostAddress();
27                    break;
28                }
29            }
30
31            if (StringUtils.isEmpty(ip)) {
32                throw new RuntimeException("cannot find available ip from"
33                        + " network interface " + networkInterface);
34            }
35
36        }
37    }
38    // 重写本地变量
39    this.overrideFromEnv(environment);
40}

链路解释:

  1. 重写本地变量。

继续看下NacosDiscoveryProperties#overridFromEnv()方法:

 1public void overrideFromEnv(Environment env) {
2
3    if (StringUtils.isEmpty(this.getServerAddr())) {
4        this.setServerAddr(env
5                .resolvePlaceholders("${spring.cloud.nacos.discovery.server-addr:}"));
6    }
7    if (StringUtils.isEmpty(this.getNamespace())) {
8        this.setNamespace(env
9                .resolvePlaceholders("${spring.cloud.nacos.discovery.namespace:}"));
10    }
11    if (StringUtils.isEmpty(this.getAccessKey())) {
12        this.setAccessKey(env
13                .resolvePlaceholders("${spring.cloud.nacos.discovery.access-key:}"));
14    }
15    if (StringUtils.isEmpty(this.getSecretKey())) {
16        this.setSecretKey(env
17                .resolvePlaceholders("${spring.cloud.nacos.discovery.secret-key:}"));
18    }
19    if (StringUtils.isEmpty(this.getLogName())) {
20        this.setLogName(
21                env.resolvePlaceholders("${spring.cloud.nacos.discovery.log-name:}"));
22    }
23    if (StringUtils.isEmpty(this.getClusterName())) {
24        this.setClusterName(env.resolvePlaceholders(
25                "${spring.cloud.nacos.discovery.clusterName-name:}"));
26    }
27    if (StringUtils.isEmpty(this.getEndpoint())) {
28        this.setEndpoint(
29                env.resolvePlaceholders("${spring.cloud.nacos.discovery.endpoint:}"));
30    }
31}

核心思想是从Environment中获取这些必须值并赋值。

Bean: NacosDiscoveryClient

根据@EnableDiscoveryClient的语义,想要实现一个客户端的服务发现,需要实现DiscoveryClient接口:

 1public interface DiscoveryClient {
2
3    /**
4     * 一个可读的实现描述,用于健康检查
5     * @return the description
6     */

7    String description();
8
9    /**
10     * 获取服务serviceId下所有服务实例
11     * @param serviceId the serviceId to query
12     * @return a List of ServiceInstance
13     */

14    List<ServiceInstance> getInstances(String serviceId);
15
16    /**
17     * 返回所有的服务serviceId
18     * @return all known service ids
19     */

20    List<String> getServices();
21
22}

NacosDiscoveryClient也实现了它的接口,我们先看下对方法的具体实现。

NacosDiscoveryClient#getInstantces():

 1@Override
2public List<ServiceInstance> getInstances(String serviceId) {
3    try {
4        // 获取该服务serviceId下的实例信息
5        List<Instance> instances = discoveryProperties.namingServiceInstance()
6                .selectInstances(serviceId, true);
7        // 封装为ServiceInstance实例
8        return hostToServiceInstanceList(instances, serviceId);
9    }
10    ...
11}

链路分析:

  1. 获取该服务serviceId下的实例信息。

  2. 封装为ServiceInstance实例。

调用NacosDiscoveryProperties#namingServiceInstance()时:

 1public NamingService namingServiceInstance() {
2
3    if (null != namingService) {
4        return namingService;
5    }
6
7    Properties properties = new Properties();
8    properties.put(SERVER_ADDR, serverAddr);
9    properties.put(NAMESPACE, namespace);
10    properties.put(UtilAndComs.NACOS_NAMING_LOG_NAME, logName);
11    properties.put(ENDPOINT, endpoint);
12    properties.put(ACCESS_KEY, accessKey);
13    properties.put(SECRET_KEY, secretKey);
14    properties.put(CLUSTER_NAME, clusterName);
15    properties.put(NAMING_LOAD_CACHE_AT_START, namingLoadCacheAtStart);
16
17    try {
18        // 根据服务信息构建命名服务
19        namingService = NacosFactory.createNamingService(properties);
20        return namingService;
21    }
22    catch (Exception e) {
23        LOGGER.error("create naming service error!properties={},e=,"this, e);
24        return null;
25    }
26}

SCA会根据我们在application.yml中使用的服务信息,构建当前服务的命名服务。

然后通过反射的形式,通过构造方法,实例化一个NamingService,NacosNamingService#NacosNamingService():

 1public NacosNamingService(Properties properties) {
2    Properties properties = new Properties();
3    // 设置Nacos服务节点集群属性
4    properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList);
5    // 初始化NacosNamingService
6    init(properties);
7}
8
9private void init(Properties properties) {
10    // 解析当前service的namespace
11    namespace = InitUtils.initNamespaceForNaming(properties);
12    // 初始化Nacos服务节点的域名地址
13    initServerAddr(properties);
14    InitUtils.initWebRootContext();
15    // NamingService的文件缓存地址
16    initCacheDir();
17    // 初始化日志对象
18    initLogName(properties);
19    // 创建事件驱动器
20    eventDispatcher = new EventDispatcher();
21    // 创建一个命名服务的代理
22    serverProxy = new NamingProxy(namespace, endpoint, serverList, properties);
23    // 利用命名服务的代理创建对应的心跳响应
24    beatReactor = new BeatReactor(serverProxy, initClientBeatThreadCount(properties));
25    // 创建一个Host响应
26    hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, isLoadCacheAtStart(properties), initPollingThreadCount(properties));
27}

链路分析:

  1. 初始化命名服务,目的在于设置一些参数的初始值。

  2. 赋值属性,根据服务本身的启动参数,对命名服务相关参数进行设置。

  3. 创建事件驱动器,事件驱动器功能是进行通知和监听事件。

  4. 创建命名服务代理,目的在于创建和Nacos服务端建立HTTP连接的代理。

  5. 创建心跳响应,用于建立和Nacos服务端所有节点的心跳连接,心跳连接的建立依托于步骤4建立的服务代理。

  6. 创建一个Host响应,用于发起和Nacos服务端节点的注册,解绑等请求,请求的建立同样依托于步骤4建立的服务代理。

我们可以看下故障转移。

故障转移

故障转移其实只有两个私有的成员变量:failoverDir和hostReactor。

  • failoverDir是故障转移的日志记录。

  • hostReactor就是我们在上文创建的发起请求的响应式处理器。

1public void init() {
2    // 用于切换刷新写入文件的任务调度
3    executorService.scheduleWithFixedDelay(new SwitchRefresher(), 0L5000L, TimeUnit.MILLISECONDS);
4    // 用于每日统计的写文件的任务调度
5    executorService.scheduleWithFixedDelay(new DiskFileWriter(), 30, DAY_PERIOD_MINUTES, TimeUnit.MINUTES);
6    ...
7    // 后备目录和日志文件的任务调度
8}

链路分析:

  1. 根据上一次变更的时间戳来判断是否需要写入当次刷新的日志文件。

  2. 写入每天文件到磁盘中。

  3. 如果当前目录不存在,创建后备日志文件和目录。

我们是从NacosDiscoveryClient#getInstances()方法到达这里的。

接下来我们继续看NacosNamingService#selectInstances()方法:

 1@Override
2public List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy, boolean subscribe) throws NacosException {
3
4    ServiceInfo serviceInfo;
5    if (subscribe) {
6        serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
7    } else {
8        serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
9    }
10    return selectInstances(serviceInfo, healthy);
11}

这里会存在一个订阅的概念,订阅的概念其实是一个异步模型,即我们每次去自己的缓存中获取指定服务列表,而服务列表的更新,则是依靠任务调度完成的。

我们发起的请求,一般会以订阅的方式进行。

而上面的实现则是根据订阅方式,决定是从缓存中直接取出指定service的服务列表,还是去Nacos服务端获取对应service的服务列表。

接下来我们继续看NacosNamingService#selectInstances()方法:

 1private List<Instance> selectInstances(ServiceInfo serviceInfo, boolean healthy) {
2    List<Instance> list;
3    if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) {
4        return new ArrayList<Instance>();
5    }
6
7    Iterator<Instance> iterator = list.iterator();
8    // 判断服务实例的状态
9    while (iterator.hasNext()) {
10        Instance instance = iterator.next();
11        if (healthy != instance.isHealthy() || !instance.isEnabled() || instance.getWeight() <= 0) {
12            iterator.remove();
13        }
14    }
15
16    return list;
17}

接下来就是判断服务实例的状态,移除处于非健康,不可用,没有分配权重的服务实例。

NacosDiscoverClient我们分析完成了,但是感觉是不是跑题了,因为我们看到的是怎么获取实例,但是我们是什么时候注册上的呢?

这其实就是一个思想误区,认为DiscoveryClient既要完成服务注册的功能,还要进行服务实例获取。

接下来我将简略的讲一下,Spring Cloud处理的核心体系。

Spring Cloud服务注册的体系

在容器初始化时,会有onRefresh()和finishRefresh()两个方法。

  • onRefresh()是对容器进行初始化。

  • finishRefresh()是结束容器化,比如开始WebServer,发布ApplicationEvent操作等。

注册就是通过发布ApplicationEvent进行的。

那么消费的就是AbstractAutoServiceRegistration类,AbstractAutoServiceRegistration#bind():

 1@EventListener(WebServerInitializedEvent.class)
2public void bind(WebServerInitializedEvent event) {
3    ApplicationContext context = event.getApplicationContext();
4    // 如果是服务端,不进行注册
5    if (context instanceof ConfigurableWebServerApplicationContext) {
6        if ("management".equals(
7                ((ConfigurableWebServerApplicationContext) context).getServerNamespace())) {
8            return;
9        }
10    }
11    // 设置端口
12    this.port.compareAndSet(0, event.getWebServer().getPort());
13    // 启动服务注册
14    this.start();
15}

链路梳理:

  1. 如果是服务端,不进行注册。

  2. 设置端口,port端口比较重要,避免并发情况下对port写错。

  3. 进行服务注册。

继续看下服务注册的步骤,AbstractAutoServiceRegistration#start():

 1public void start() {
2    // 校验服务发现状态
3    if (!isEnabled()) {
4        if (logger.isDebugEnabled()) {
5            logger.debug("Discovery Lifecycle disabled. Not starting");
6        }
7        return;
8    }
9
10    // 没有注册,才会注册
11    if (!this.running.get()) {
12        // 获取注册信息,并发布注册前事件
13        this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));
14        // 注册
15        register();
16        // 是否应该注册服务者
17        if (shouldRegisterManagement()) {
18            registerManagement();
19        }
20        // 获取注册信息,并发布注册后事件
21        this.context.publishEvent(
22                new InstanceRegisteredEvent<>(this, getConfiguration()));
23        // 注册状态更改为已注册。
24        this.running.compareAndSet(falsetrue);
25    }
26
27}

链路分析:

  1. 校验服务发现状态。

  2. 没有注册,才会进行注册。

  3. 获取注册信息,并发布注册前事件。

  4. 注册。

  5. 是否应该注册服务则。

  6. 获取注册信息,并发布注册后事件。

  7. 注册状态更改为已注册。

从类名分析,我们不难发现,AbstractAutoServiceRegistration应该是一个超类,我们之前分析的是抽象模型,那么调用register的时候会先走派生类,NacosAutoServiceRegistration#register():

 1@Override
2protected void register() {
3    // 根据当前服务发现的属性判断是否可以注册
4    if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
5        LOGGER.debug("Registration disabled.");
6        return;
7    }
8    // 如果注册器里面的端口是不正常的,设置端口号
9    if (this.registration.getPort() < 0) {
10        this.registration.setPort(getPort().get());
11    }
12    // 调用超类的注册接口
13    super.register();
14}

链路分析:

  1. 根据当前服务发现的属性判断是否可以注册。

  2. 如果注册器里面的端口是不正常的,设置端口。

  3. 调用超类的注册接口。

继续,:

1protected void register() {
2    this.serviceRegistry.register(getRegistration());
3}

链路分析:

  1. 进行服务注册。

我们看到服务注册对象ServiceRegistry是个接口,那么继续看实现,NacosServiceRegistry#register():

 1@Override
2public void register(NacosRegistration registration) {
3    // 再次判断是否可注册
4    if (!registration.isRegisterEnabled()) {
5        logger.info("Nacos Registration is disabled...");
6        return;
7    }
8    // 如果没有服务ServiceId可供注册,不注册
9    if (StringUtils.isEmpty(registration.getServiceId())) {
10        logger.info("No service to register for nacos client...");
11        return;
12    }
13    // 获取命名服务
14    NamingService namingService = registration.getNacosNamingService();
15    // 获取服务ServiceId
16    String serviceId = registration.getServiceId();
17
18    // 创建注册实例
19    Instance instance = new Instance();
20    instance.setIp(registration.getHost());
21    instance.setPort(registration.getPort());
22    instance.setWeight(registration.getRegisterWeight());
23    instance.setCluster(new Cluster(registration.getCluster()));
24    instance.setMetadata(registration.getMetadata());
25
26    try {
27        // 进行服务注册
28        namingService.registerInstance(serviceId, instance);
29        logger.info("nacos registry, {} {}:{} register finished", serviceId,
30                instance.getIp(), instance.getPort());
31    }
32    catch (Exception e) {
33        ...
34    }
35}

链路分析:

  1. 再次判断当前服务是否可注册。

  2. 如果没有服务ServiceId,就不需要注册。

  3. 获取命名服务,命名服务在实例化EnableDiscoveryClient的时创建。

  4. 获取服务ServiceId。

  5. 创建注册实例Instance对象。

  6. 进行服务注册。

看到NacosNamingService,从前面的链路分析来看,我们接下来应该是就是使用HostReactor进行注册了,

NacosNamingService#registerInstance():

 1@Override
2public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
3    // 如果是临时节点,也就是我们需要注册的服务节点,则需要客户端自己负责心跳上报
4    if (instance.isEphemeral()) {
5        BeatInfo beatInfo = new BeatInfo();
6        beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
7        beatInfo.setIp(instance.getIp());
8        beatInfo.setPort(instance.getPort());
9        beatInfo.setCluster(instance.getClusterName());
10        beatInfo.setWeight(instance.getWeight());
11        beatInfo.setMetadata(instance.getMetadata());
12        beatInfo.setScheduled(false);
13        beatInfo.setPeriod(instance.getInstanceHeartBeatInterval());
14        // 添加心跳任务
15        beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
16    }
17    // 通过NamingProxy向Nacos服务端发起注册请求
18    serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
19}

链路分析:

  1. 如果注册的是我们的服务节点,则节点类型是临时节点,需要客户端自己负责心跳上报,心跳上报是由任务调度器来完成的。

  2. 通过NamingProxy向Nacos服务端发起服务注册请求。

接下来,NamingProxy#registerService():

 1public void registerService(String serviceName, Instance instance) throws NacosException {
2    NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}",
3                       namespaceId, serviceName, instance);
4    // 服务注册请求参数
5    final Map<String, String> params = new HashMap<String, String>(9);
6    params.put(CommonParams.NAMESPACE_ID, namespaceId);
7    params.put(CommonParams.SERVICE_NAME, serviceName);
8    params.put(CommonParams.GROUP_NAME, groupName);
9    params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
10    params.put("ip", instance.getIp());
11    params.put("port", String.valueOf(instance.getPort()));
12    params.put("weight", String.valueOf(instance.getWeight()));
13    params.put("enable", String.valueOf(instance.isEnabled()));
14    params.put("healthy", String.valueOf(instance.isHealthy()));
15    params.put("ephemeral", String.valueOf(instance.isEphemeral()));
16    params.put("metadata", JSON.toJSONString(instance.getMetadata()));
17    // 发起HTTP请求,请求方法:/nacos/v1/ns/instance
18    reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST);
19}

链路分析:

  1. 将Instance实例对象转化为HashMap参数对象,调用HttpClient的请求方法,进行注册。由于是注册行为,所以Http请求方法为PUT。

这样,我们的服务发现的实例化与注册流程基本打通了。

聪明的你们肯定会发现,在刚刚的注册链路中,有两个Bean是不知道何时进行实例化的。

NacosServiceRegistry

追根朔源,我们发现NacosServiceRegistry是在NacosDiscoveryAutoConfiguration类中进行初始化的。

首先,NacosDiscoveryAutoConfiguration类头会有一系列的校验规则:

1@Configuration
2@EnableConfigurationProperties
3@ConditionalOnNacosDiscoveryEnabled
4@ConditionalOnClass(name = "org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent")
5@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
6@AutoConfigureBefore({ AutoServiceRegistrationAutoConfiguration.class,
7        NacosDiscoveryClientAutoConfiguration.class })

校验规则分析:

  1. 是个Configuration配置类,意味着接下来需要注入多个Bean。

  2. 允许使用配置属性信息。

  3. 允许进行Nacos服务发现。

  4. ServletWebServerInitializedEvent实例化之后实例化。

  5. 校验自动注册规则,默认值为true。

  6. 在实例化之前,先完成AutoServiceRegistrationAutoConfiguration和NacosDiscoveryClientAutoConfiguration的配置。

峰回路转,我们看到了NacosDiscoveryClientAutoConfiguration类。

再看NacosDiscoveryAutoConfiguration中具体的Bean,逐个进行分析:

1@Bean
2public NacosServiceRegistry nacosServiceRegistry() {
3    return new NacosServiceRegistry();
4}

第一个是实现ServiceRegistry接口的NacosServiceRegistry,我们在注册链路中刚刚用过它的register()方法。

它共有两个核心方法,分别是register()和deregister()。

1@Override
2public void deregister(NacosRegistration registration) {
3    if (instance.isEphemeral()) {
4        beatReactor.removeBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), instance.getIp(), instance.getPort());
5    }
6    serverProxy.deregisterService(NamingUtils.getGroupedName(serviceName, groupName), instance);
7}

调用链路:

  1. 如果需要解绑的实例是我们的服务实例,则需要移除后台正在进行调度的心跳上报任务。

  2. 通过NamingProxy向Nacos服务端发起解绑服务请求。

下一个Bean:

1@Bean
2@ConditionalOnBean(AutoServiceRegistrationProperties.class)
3public NacosRegistration nacosRegistration() {
4    return new NacosRegistration();
5}

这个对象也很熟悉,在注册链路和撤销注册链路中,这个对象就是入参。

我们在这里进行一些默认属性的配置:

 1@PostConstruct
2public void init() {
3
4    Environment env = context.getEnvironment();
5    Integer managementPort = ManagementServerPortUtils.getPort(context);
6    if (null != managementPort) {
7        Map<String, String> metadata = nacosDiscoveryProperties.getMetadata();
8        metadata.put(MANAGEMENT_PORT, managementPort.toString());
9        String contextPath = env
10                .getProperty("management.server.servlet.context-path");
11        String address = env.getProperty("management.server.address");
12        if (!StringUtils.isEmpty(contextPath)) {
13            metadata.put(MANAGEMENT_CONTEXT_PATH, contextPath);
14        }
15        if (!StringUtils.isEmpty(address)) {
16            metadata.put(MANAGEMENT_ADDRESS, address);
17        }
18    }
19}

因为我们需要在NacosRegistration中使用NacosDiscoveryProperties属性,所以我们就先需要进行NacosDiscoveryClientAutoConfiguration的注入。

下一个Bean:

1@Bean
2@ConditionalOnBean(AutoServiceRegistrationProperties.class)
3public NacosAutoServiceRegistration nacosAutoServiceRegistration(
4        NacosServiceRegistry registry,
5        AutoServiceRegistrationProperties autoServiceRegistrationProperties,
6        NacosRegistration registration)
 
{
7    return new NacosAutoServiceRegistration(registry,
8            autoServiceRegistrationProperties, registration);
9}

这个Bean出现在注册链路的前端,在容器执行finishRefresh()并发送ApplicationEvent事件时,此时,NacosAutoServiceRegistration是AbstractAutoServiceRegistration的派生类。

至此,结束SCA的服务注册源码分析。

Spring Cloud和Spring Boot是两条链路,开发进度不同,故不要使用最新的Spring Boot版本,应该框架建议的Spring Boot版本,当前SCA代码中Spring Boot版本为2.1.1.RELEASE。

总结

我们以更直观的形式进行总结:

                            Nacos服务注册流程