springcloudAlibaba之Nacos服务注册源码分析

Nacos服务注册源码分析

  • Nacos服务注册原理
  • Nacos服务注册怎么做的
  • Nacos服务注册源码解读

带着以上三个问题,进入到今天的源码解读

问题1:Nacos服务注册原理

Nacos首先从bootstrap.yml配置文件中读取我们配置好的nacos配置,这里面一般包括spring.application.name、spring.cloud.nacos.discovery.server-addr、spring.cloud.nacos.username、spring.cloud.nacos.password以及其他的一些例如cluster、namespace等信息,依托ConfigurationProperties注解 让spring将这些配置封装成一个对象,在容器刷新完成之后执行一个register方法,把当前应用注册到指定的Nacos服务端

问题2:Nacos服务注册怎么做的

我们知道 像这种第三方集成springboot的应用,一般都会使用starter封装起来,而starter的核心就是自动装配,那么我们只需要搜索类似nacosAutoconfig之类的 就应该可以找到

springcloudAlibaba之Nacos服务注册源码分析_第1张图片

不出所料 我们成功搜索到了几个类,大致可以分为三类 config配置中心、discovery注册中心以及ribbon负载均衡 ,目前我们关注的是注册中心 那么直接选择NacosDiscoveryAutoConfiguration即可。

@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(
    value = {"spring.cloud.service-registry.auto-registration.enabled"},
    matchIfMissing = true
)
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class, AutoServiceRegistrationAutoConfiguration.class})
public class NacosDiscoveryAutoConfiguration {
    public NacosDiscoveryAutoConfiguration() {
    }

    @Bean
    public NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
        return new NacosServiceRegistry(nacosDiscoveryProperties);
    }

    @Bean
    @ConditionalOnBean({AutoServiceRegistrationProperties.class})
    public NacosRegistration nacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties, ApplicationContext context) {
        return new NacosRegistration(nacosDiscoveryProperties, context);
    }

    @Bean
    @ConditionalOnBean({AutoServiceRegistrationProperties.class})
    public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
        return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
    }
}

通过观察这个类 可以看出来它是一个典型的springboot配置类,它里面干了三件事,也可以说是一件事,那就是注册Bean,第一、第二个bean是第三个bean的入参 ,说明第三个bean是重点,点进去之后发现这个类里面有一个register方法,盲猜 这个应该就是用来注册的方法,registration对象就是我们配置文件中的配置信息

    public void register(Registration registration) {
        if (StringUtils.isEmpty(registration.getServiceId())) {
            log.warn("No service to register for nacos client...");
        } else {
            String serviceId = registration.getServiceId();
            Instance instance = new Instance();
            instance.setIp(registration.getHost());
            instance.setPort(registration.getPort());
            instance.setWeight((double)this.nacosDiscoveryProperties.getWeight());
            instance.setClusterName(this.nacosDiscoveryProperties.getClusterName());
            instance.setMetadata(registration.getMetadata());

            try {
                this.namingService.registerInstance(serviceId, instance);
                ...
            } catch (Exception var5) {
                ...
            }

        }
    }

那么是谁来调用这个register方法呢?这个我们后面再说。回到NacosDiscoveryAutoConfiguration类,这里面有几个注解需要关注,这几个注解定义了这个类执行的前提条件

@Configuration  #声明这是一个springboot配置类
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled#条件注解:只有NacosDiscoveryEnabledtrue时 这个类才生效
@ConditionalOnProperty(value = {"spring.cloud.service-registry.auto-registration.enabled"},
     				   matchIfMissing = true
                     )#和上面一样 只有配置文件中enabled为true时 这个类才生效 默认为true 如何使用其他框架例如dubbo进行服务注册 需要把这个设为false 要不然就会注册两个相同的服务
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class,
                     AutoServiceRegistrationAutoConfiguration.class})#声明需要在这两个bean创建之后进行加载
    
@ConditionalOnBean({AutoServiceRegistrationProperties.class})#容器中存在这个bean才会生效

问题3:Nacos服务注册源码解读

通过第二步的探究,我们锁定了register这个方法,那么是由谁来调用它的呢?

通过debug的方式 我们从控制台看到了这样的堆栈信息

springcloudAlibaba之Nacos服务注册源码分析_第2张图片

通过堆栈信息可以清楚的看出来 它是从spring核心方法refresh方法中的finishRefresh方法进入的,它的大致链路就是refresh–>finishRefresh–>this.getLifecycleProcessor().onRefresh()–>startBeans(这个方法有一个while循环 当beanName是webServerStartStop时 走到下面链路)

–>((DefaultLifecycleProcessor.LifecycleGroup)phases.get(key)).start()

–>DefaultLifecycleProcessor.this.doStart(this.lifecycleBeans, member.name, this.autoStartupOnly);

–>this.doStart(lifecycleBeans, dependency, autoStartupOnly);

–>bean.start()

–>this.applicationContext.publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));

–>this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);

–>this.invokeListener(listener, event);

–this.doInvokeListener(listener, event);

–>listener.onApplicationEvent(event);

–>AbstractAutoServiceRegistration.onApplicationEvent.this.bind(event)

–this.start()

–this.register();

–this.serviceRegistry.register(this.getRegistration());

Nacos巧妙的运用springboot提供的事件订阅和发布机制,使得springboot应用在容器启动完成后第一时间调用register方法 将当前应用信息作为服务注册到nacos服务端

总结

首先springboot应用启动时会自动加载一个beanNamewebServerStartStop,这个bean对应的类是WebServerStartStopLifecycle,这个类又实现了SmartLifecycle这个接口,关于lifecycle接口的解释,借用网上的一段话

Lifecycle是Spring中最基础的生命周期接口,该接口定义了容器启动和停止的方法。方便开发者扩展自己的特定逻辑,比如启动和停止某些后台进程。Lifecycle常用来管理一个组件的启动和停止,可能会有这样的疑惑:开始和停止的逻辑写在一个bean的初始化方法和销毁方法中不可以了吗,为什么要实现个Lifecycle接口呢?这里说明一下,bean的初始化方法和销毁方法是Bean生命周期级别的;而Lifecycle是容器生命周期级别的。

它的意思就是 只有当容器启动和关闭的时候 ,springboot应用才会调用实现了这些接口的类

当我们context刷新成功之后会调用finishRefresh方法

protected void finishRefresh() {
    this.clearResourceCaches();
    this.initLifecycleProcessor();#在这里把所有实现了lifecycle的bean进行加载
    this.getLifecycleProcessor().onRefresh();#在这里把上面加载好的bean一一执行
    this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
    LiveBeansView.registerApplicationContext(this);
}

里面就有一个WebServerStartStopLifecycle这个bean,按照设定 会去调用这个bean的start方法

public void start() {
    this.webServer.start();
    this.running = true;
    this.applicationContext.publishEvent(
        new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
}

这里关键的是最后的publishEvent方法,它会去发布一个事件,并将满足条件的监听器进行依次执行,从代码中可以看出 只要是ServletWebServerInitializedEvent相关的且实现了ApplicationListener的类 都会被执行,而ServletWebServerInitializedEvent又是WebServerInitializedEvent的子类,换句话讲 只要是WebServerInitializedEvent相关的类 都会被执行。

巧合的是AbstractAutoServiceRegistration这个类实现了ApplicationListener,并且入参泛型就是WebServerInitializedEvent,那么一定会执行到这个类中的方法。那么这个类是哪来的呢?回到最上面自动装配的那块,nacos自动装配的3个bean,其中最后一个beanNacosAutoServiceRegistration的父类就是AbstractAutoServiceRegistration

在执行满足条件的监听器方法时,会调用onApplicationEvent方法,然后到bind方法、start方法,最终在start方法中调用this.register()方法,调用的就是ServiceRegistryregister方法,而当前应用有且只有一个ServiceRegistry的实现类,就是NacosServiceRegistry,最终成功执行register方法,至此 nacos成功注册。

public abstract class AbstractAutoServiceRegistration<R extends Registration> implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {
    private static final Log logger = LogFactory.getLog(AbstractAutoServiceRegistration.class);
    private final ServiceRegistry<R> serviceRegistry;
    private boolean autoStartup = true;
    private AtomicBoolean running = new AtomicBoolean(false);
    private int order = 0;
    private ApplicationContext context;
    private Environment environment;
    private AtomicInteger port = new AtomicInteger(0);
    private AutoServiceRegistrationProperties properties;

    protected void register() {
        this.serviceRegistry.register(this.getRegistration());
    }
    
    public void onApplicationEvent(WebServerInitializedEvent event) {
        this.bind(event);
    }
    /** @deprecated */
    @Deprecated
    public void bind(WebServerInitializedEvent event) {
        ApplicationContext context = event.getApplicationContext();
        if (!(context instanceof ConfigurableWebServerApplicationContext) || !"management".equals(((ConfigurableWebServerApplicationContext)context).getServerNamespace())) {
            this.port.compareAndSet(0, event.getWebServer().getPort());
            this.start();
        }
    }
    public void start() {
        if (!this.isEnabled()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Discovery Lifecycle disabled. Not starting");
            }

        } else {
            if (!this.running.get()) {
                this.context.publishEvent(new InstancePreRegisteredEvent(this, this.getRegistration()));
                this.register();
                if (this.shouldRegisterManagement()) {
                    this.registerManagement();
                }

                this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));
                this.running.compareAndSet(false, true);
            }

        }
        ........
    }
}

你可能感兴趣的:(spring,boot,springcloud)