• 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之类的 就应该可以找到

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    不出所料 我们成功搜索到了几个类,大致可以分为三类 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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    通过观察这个类 可以看出来它是一个典型的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) {
                    ...
                }
    
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    那么是谁来调用这个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才会生效
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

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

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

    在这里插入图片描述

    通过堆栈信息可以清楚的看出来 它是从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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    public void start() {
        this.webServer.start();
        this.running = true;
        this.applicationContext.publishEvent(
            new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这里关键的是最后的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);
                }
    
            }
            ........
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
  • 相关阅读:
    基于Python实现的钢筋数量识别
    Kotlin Flow响应式编程,操作符函数进阶
    近地面无人机植被定量遥感与生理参数反演
    UiPath:一家由生成式AI驱动的流程自动化软件公司
    性能优化-如何爽玩多线程来开发
    车辆识别信息易语言代码
    【计算机组成原理】海明校验码
    【小程序】统计字符
    发布 jar 包到 maven 中央仓库
    java/php/python/nodejs+vue景区失物招领网站平台
  • 原文地址:https://blog.csdn.net/Amazing66/article/details/133553375