searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Nacos 启动流程源码分析

2024-06-21 09:38:23
92
0

1.SpringBoot 的启动流程

SpringBoot 启动流程中主要完成的动作如下:

1. 执行SpringApplication构造方法,初始化属性。判断应用类型是REACTIVE还是SERVLET。读取spring.factories文件加载初始化器ApplicationContextInitializer和监听器ApplicationListener

2. 实例调用run方法,记录启动时间,通过SpringFactoriesLoader加载Listeners,发布SpringBoot starting事件。

3. 准备环境变量,创建和配置environment,包含系统属性和用户配置的属性等,发布事件SpringApplicationRunListeners#environmentPrepared

4. 打印SpringBootbanner和版本。

5. 创建对应类型的应用程序的上下文ApplicationContext

6. 准备ApplicationContext以及实例化bean对象,然后发布事件SpringApplicationRunListeners# contextPrepared打印启动日志和Profile;增加懒加载处理器,然后初始化Bean,再发布contextLoaded事件。

7. 刷新上下文:refreshContext主要就是Spring IOC容器的创建过程,并且会进行自动装配,ApplicationContext发布refresh事件,说明ApplicationContext初始化完成。onRefresh() 内部创建应用容器tomcatfinishRefresh()中将会启动tomcat

8. 计算启动耗时,Listeners发布started事件,表示启动成功

9. 回调所有的ApplicationRunnerCommandLineRunner

10. 发布Ready事件ApplicationReadyEvent,表示SpringBoot可以接收处理的请求。

SpringApplication .run() 方法主要代码

   public ConfigurableApplicationContext run(String... args) {
        long startTime = System.nanoTime();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }

            listeners.started(context, timeTakenToStartup);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }

        try {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            listeners.ready(context, timeTakenToReady);
            return context;
        } catch (Throwable var11) {
            this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var11);
        }
    }

 

1.1 SpringBoot的生命周期

 SpringApplicationRunListener 接口规定了SpringBoot的生命周期,在各个生命周期广播相应的事件,调用实际的ApplicationListener类。如下为每个生命周期阶段以及对应的方法,其中started running方法在2.6.6版本的SpringBoot中已经被标记为Deprecated.

public interface SpringApplicationRunListener {

    // 在run()方法开始执行时,该方法就立即被调用,可用于在初始化最早期时做一些工作
    void starting();
    // 当environment构建完成,ApplicationContext创建之前,该方法被调用
    void environmentPrepared(ConfigurableEnvironment environment);
    // 当ApplicationContext构建完成时,该方法被调用
    void contextPrepared(ConfigurableApplicationContext context);
    // 在ApplicationContext完成加载,但没有被刷新前,该方法被调用
    void contextLoaded(ConfigurableApplicationContext context);
    // 在ApplicationContext刷新并启动后,CommandLineRunners和ApplicationRunner未被调用前,该方法被调用
    void started(ConfigurableApplicationContext context);
    // 在run()方法执行完成前该方法被调用
    void running(ConfigurableApplicationContext context);
    // 当应用运行出错时该方法被调用
    void failed(ConfigurableApplicationContext context, Throwable exception);
}

 

2. Nacos 加载和初始化

2.1 Nacos 的初始化

实现SpringApplicationRunListener ,并将自定义实现的Listener加入到Listener列表,实现生命周期扩展

 

2.1.1 Starting 阶段, 设置为startingtrue,表示开始启动

2.1.2 environmentPrepared阶段

LoggingApplicationListener

· 加载logging.config

 

StartingApplicationListener

· 创建工作目录

· 注入环境变量,主要是系统属性和运行时参数

· application.properties加载配置,并注册监听文件

· 系统属性初始化,确定启动模式和本地IP

 

2.1.3 contextPrepared阶段之前applyInitializers初始化,在PropertyUtil初始化Nacos的设置

比较重要的是确定存储方式:内置derby还是外置数据库。

    

    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        this.applyInitializers(context);
        listeners.contextPrepared(context);
        bootstrapContext.close(context);
        
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
            ((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
        }

        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }

        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

 

2.1.4 contextPrepared阶段StartingApplicationListener cluster.conf加载节点信息, 开始打印Starting信息。然后加载LazyInitializationBeanFactory

 

2.1.5 refreshContext 刷新上下文,主要代码如下,其主要功能是加载bean

 

2.1.5.1 auth 模块

AuthConfigs 类 构造器初始化AuthConfigs对象,获取nacos.core.auth.plugin 相关配置

2.1.5.2 console 模块

ConsoleConfig 配置类初始化,设置加载controller类的包路径,顺序依次是core namingconfigconsole, 实际上还有auth插件的controller, 应该是懒加载了。

@PropertySource("/application.properties")
public class ConsoleConfig {
    /**
     * Init.
     */
    @PostConstruct
    public void init() {
        methodsCache.initClassMethod("com.alibaba.nacos.core.controller");
        methodsCache.initClassMethod("com.alibaba.nacos.naming.controllers");
        methodsCache.initClassMethod("com.alibaba.nacos.config.server.controller");
        methodsCache.initClassMethod("com.alibaba.nacos.console.controller");
    }

2.1.5.3 core模块

· 初始化ServerMemberManager 构造初始化设置ContentPathnacos,设置当前所在节点的信息,将自己加入集群并发布一个MemberChangeEvent事件广播节点发生了变化,然后注册监听本地IP变更事件,如果发生变更,则将旧的地址移除,加入新的地址。

initAndStartLookup是采用工厂模式创建成员节点地址的监测,分为三种模式: address-server即从远程读取和更新
FileConfig 是读取和监听cluster.conf文件,standalone模式这是直接获取本地地址比较然后更新。
     

DistroMapper

ProtocolManager 注册监听 ServerMemberManager变化


2.1.5.4 naming 模块

RaftCore  构造,基本信息的初始化

ConsistencyService  一致性服务实现了的初始化。


ServiceManager  管理服务的核心类,有一个全局的执行器监听服务meta,以保证服务meta数据的一致性。

DistroProtocol初始化,如果是standalone模式启动,则跳过。如果是集群模式启动,则使用线程池校验个节点时间和数据。

UpgradeJudgement  判断是否正在进行升级流程,如果是则初始化升级检查订阅检查升级。

ServerStatusManager  构造对象并注册Server状态更新对象,当本地Server发生变化时,更新server状态

SwitchManager  初始化并注册监听SwitchDomain 变更

ServerListManager   节点列表管理器初始化,订阅节点信息,订阅成员信息上报和更新。


2.1.5.5 config模块

CapacityService   初始化容量管理服务,本意是要限制创建的命名空间数量和配置数量,实际上目前并没有启用限制。

ConnectionManager 连接管理器,构造函数中注册监听连接限制规则变更事件。初始化先从本地加载 data/loader/limitRule ,如果目录不存在则创建。并注册监听 data/loader目录,如果 data/loader/limitRule文件发生变化,则重新载入文件内容。

 

DumpService 构造函数,这里依赖persistService 来持久化到数据库,依赖ServerMemberManager来获取节点变更情况,将构造的对象作为参数,创建了4Processor。这些DumpProcessor的主要作用是将更新配置、灰度、tag等数据持久化到磁盘。然后创建两个通用的TaskManager, 将前面创建的Processor作为参数传入,由Task管理Processor的执行。最后获取数据源,根据Condition条件,初始化Jdbc,将初始化状态设为true.

 

EmbeddedDumpService  构造函数创建 EmbeddedDumpService对象,初始化函数执行流程如下:

getCpProtocol中初始化Jraft Executer,其中各个Executor的线程数默认依次为 raftCoreExecutor  4raftCliServiceExecutor 4raftCommonExecutor  8raftSnapshotExecutor 4

protocolManager.getCpProtocol(),默认是CP模式启动,这里会初始化协议,通过MemberManager将节点信息注入到协议中,构造JRaftServerJRaftProtocol对象并初始化,其中包括创建raft executer,加载raft相关配置,创建raft节点和group,详细内容见Raft初始化章节,最后启动rpcServer

raftServer启动成功,但此时还需要等待创建group和选举成功后才能执行dump操作。

   @PostConstruct
    @Override
    protected void init() throws Throwable {
		//如果是standalone模式启动,直接执行dump操作然后返回
        if (EnvUtil.getStandaloneMode()) {
            dumpOperate(processor, dumpAllProcessor, dumpAllBetaProcessor, dumpAllTagProcessor);
            return;
        }
        //默认是CP模式启动,这里会初始化协议,通过MemberManager将节点信息注入到协议中,构造JRaftServer和JRaftProtocol对象并初始化,其中包括创建raft  executer,加载raft相关配置,创建raft节点和group,详细内容见Raft初始化章节,最后启动rpcServer
       CPProtocol protocol = protocolManager.getCpProtocol();
        AtomicReference<Throwable> errorReference = new AtomicReference<>(null);
        CountDownLatch waitDumpFinish = new CountDownLatch(1);
        
        // watch path => /nacos_config/leader/ has value ?
        Observer observer = new Observer() {
            
            @Override
            public void update(Observable o) {
                if (!(o instanceof ProtocolMetaData.ValueItem)) {
                    return;
                }
                final Object arg = ((ProtocolMetaData.ValueItem) o).getData();
                GlobalExecutor.executeByCommon(() -> {
                    // must make sure that there is a value here to perform the correct operation that follows
                    if (Objects.isNull(arg)) {
                        return;
                    }
                    // Identify without a timeout mechanism
                    EmbeddedStorageContextUtils.putExtendInfo(Constants.EXTEND_NEED_READ_UNTIL_HAVE_DATA, "true");
                    // Remove your own listening to avoid task accumulation
                    boolean canEnd = false;
                    for (; ; ) {
                        try {
							// 从leaderdump数据到本地
                            dumpOperate(processor, dumpAllProcessor, dumpAllBetaProcessor, dumpAllTagProcessor);
                            //
                            protocol.protocolMetaData()
                                    .unSubscribe(Constants.CONFIG_MODEL_RAFT_GROUP, MetadataKey.LEADER_META_DATA, this);
                            canEnd = true;
                        } catch (Throwable ex) {
                            if (!shouldRetry(ex)) {
                                errorReference.set(ex);
                                canEnd = true;
                            }
                        }
                        if (canEnd) {
                            ThreadUtils.countDown(waitDumpFinish);
                            break;
                        }
                        ThreadUtils.sleep(500L);
                    }
                    EmbeddedStorageContextUtils.cleanAllContext();
                });
            }
        };
        //observer订阅nacos_config   leader metadata 
        protocol.protocolMetaData()
                .subscribe(Constants.CONFIG_MODEL_RAFT_GROUP, MetadataKey.LEADER_META_DATA, observer);
        
        //这里必须要等待dump任务完成回调,才能继续进行初始化
        ThreadUtils.latchAwait(waitDumpFinish);
        
        // If an exception occurs during the execution of the dump task, the exception
        // needs to be thrown, triggering the node to start the failed process
        final Throwable ex = errorReference.get();
        if (Objects.nonNull(ex)) {
            throw ex;
        }
    }

PersistentClientOperationServiceImpl   初始化,调用 raftServer createMultiRaftGroup方法创建naming_persistent_service_v2 ,创建group的详细流程见raft初始化

InstanceMetadataProcessor            初始话,,调用 raftServer createMultiRaftGroup方法创建naming_instance_metadata,创建group的详细流程见raft初始化

ServiceMetadataProcessor  构造,调用 raftServer createMultiRaftGroup方法创建naming_service_metadata,创建group的详细流程见3.3节。

2.1.5.6 plugin-default-impl模块

JwtTokenManager  初始化,确定认证方式和认证相关配置信息。

2.1.6 afterRefresh

说明ApplicationContext初始化Bean已完成,启动Tomcat, 绑定端口启动成功

2.1.7 started 阶段

关闭启动过程中的定时任务。
        设置启动状态为true,表示已启动。
       打印启动模式和存储模式。

2.1.7 ready阶段

启动已完成。


本篇主要记录了Nacos 启动执行初始化的主要流程,下一篇介绍Nacos启动过程中非常重要的raft协议的初始化过程。

0条评论
0 / 1000
牧童
1文章数
0粉丝数
牧童
1 文章 | 0 粉丝
牧童
1文章数
0粉丝数
牧童
1 文章 | 0 粉丝
原创

Nacos 启动流程源码分析

2024-06-21 09:38:23
92
0

1.SpringBoot 的启动流程

SpringBoot 启动流程中主要完成的动作如下:

1. 执行SpringApplication构造方法,初始化属性。判断应用类型是REACTIVE还是SERVLET。读取spring.factories文件加载初始化器ApplicationContextInitializer和监听器ApplicationListener

2. 实例调用run方法,记录启动时间,通过SpringFactoriesLoader加载Listeners,发布SpringBoot starting事件。

3. 准备环境变量,创建和配置environment,包含系统属性和用户配置的属性等,发布事件SpringApplicationRunListeners#environmentPrepared

4. 打印SpringBootbanner和版本。

5. 创建对应类型的应用程序的上下文ApplicationContext

6. 准备ApplicationContext以及实例化bean对象,然后发布事件SpringApplicationRunListeners# contextPrepared打印启动日志和Profile;增加懒加载处理器,然后初始化Bean,再发布contextLoaded事件。

7. 刷新上下文:refreshContext主要就是Spring IOC容器的创建过程,并且会进行自动装配,ApplicationContext发布refresh事件,说明ApplicationContext初始化完成。onRefresh() 内部创建应用容器tomcatfinishRefresh()中将会启动tomcat

8. 计算启动耗时,Listeners发布started事件,表示启动成功

9. 回调所有的ApplicationRunnerCommandLineRunner

10. 发布Ready事件ApplicationReadyEvent,表示SpringBoot可以接收处理的请求。

SpringApplication .run() 方法主要代码

   public ConfigurableApplicationContext run(String... args) {
        long startTime = System.nanoTime();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }

            listeners.started(context, timeTakenToStartup);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }

        try {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            listeners.ready(context, timeTakenToReady);
            return context;
        } catch (Throwable var11) {
            this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var11);
        }
    }

 

1.1 SpringBoot的生命周期

 SpringApplicationRunListener 接口规定了SpringBoot的生命周期,在各个生命周期广播相应的事件,调用实际的ApplicationListener类。如下为每个生命周期阶段以及对应的方法,其中started running方法在2.6.6版本的SpringBoot中已经被标记为Deprecated.

public interface SpringApplicationRunListener {

    // 在run()方法开始执行时,该方法就立即被调用,可用于在初始化最早期时做一些工作
    void starting();
    // 当environment构建完成,ApplicationContext创建之前,该方法被调用
    void environmentPrepared(ConfigurableEnvironment environment);
    // 当ApplicationContext构建完成时,该方法被调用
    void contextPrepared(ConfigurableApplicationContext context);
    // 在ApplicationContext完成加载,但没有被刷新前,该方法被调用
    void contextLoaded(ConfigurableApplicationContext context);
    // 在ApplicationContext刷新并启动后,CommandLineRunners和ApplicationRunner未被调用前,该方法被调用
    void started(ConfigurableApplicationContext context);
    // 在run()方法执行完成前该方法被调用
    void running(ConfigurableApplicationContext context);
    // 当应用运行出错时该方法被调用
    void failed(ConfigurableApplicationContext context, Throwable exception);
}

 

2. Nacos 加载和初始化

2.1 Nacos 的初始化

实现SpringApplicationRunListener ,并将自定义实现的Listener加入到Listener列表,实现生命周期扩展

 

2.1.1 Starting 阶段, 设置为startingtrue,表示开始启动

2.1.2 environmentPrepared阶段

LoggingApplicationListener

· 加载logging.config

 

StartingApplicationListener

· 创建工作目录

· 注入环境变量,主要是系统属性和运行时参数

· application.properties加载配置,并注册监听文件

· 系统属性初始化,确定启动模式和本地IP

 

2.1.3 contextPrepared阶段之前applyInitializers初始化,在PropertyUtil初始化Nacos的设置

比较重要的是确定存储方式:内置derby还是外置数据库。

    

    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        this.applyInitializers(context);
        listeners.contextPrepared(context);
        bootstrapContext.close(context);
        
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
            ((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
        }

        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }

        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

 

2.1.4 contextPrepared阶段StartingApplicationListener cluster.conf加载节点信息, 开始打印Starting信息。然后加载LazyInitializationBeanFactory

 

2.1.5 refreshContext 刷新上下文,主要代码如下,其主要功能是加载bean

 

2.1.5.1 auth 模块

AuthConfigs 类 构造器初始化AuthConfigs对象,获取nacos.core.auth.plugin 相关配置

2.1.5.2 console 模块

ConsoleConfig 配置类初始化,设置加载controller类的包路径,顺序依次是core namingconfigconsole, 实际上还有auth插件的controller, 应该是懒加载了。

@PropertySource("/application.properties")
public class ConsoleConfig {
    /**
     * Init.
     */
    @PostConstruct
    public void init() {
        methodsCache.initClassMethod("com.alibaba.nacos.core.controller");
        methodsCache.initClassMethod("com.alibaba.nacos.naming.controllers");
        methodsCache.initClassMethod("com.alibaba.nacos.config.server.controller");
        methodsCache.initClassMethod("com.alibaba.nacos.console.controller");
    }

2.1.5.3 core模块

· 初始化ServerMemberManager 构造初始化设置ContentPathnacos,设置当前所在节点的信息,将自己加入集群并发布一个MemberChangeEvent事件广播节点发生了变化,然后注册监听本地IP变更事件,如果发生变更,则将旧的地址移除,加入新的地址。

initAndStartLookup是采用工厂模式创建成员节点地址的监测,分为三种模式: address-server即从远程读取和更新
FileConfig 是读取和监听cluster.conf文件,standalone模式这是直接获取本地地址比较然后更新。
     

DistroMapper

ProtocolManager 注册监听 ServerMemberManager变化


2.1.5.4 naming 模块

RaftCore  构造,基本信息的初始化

ConsistencyService  一致性服务实现了的初始化。


ServiceManager  管理服务的核心类,有一个全局的执行器监听服务meta,以保证服务meta数据的一致性。

DistroProtocol初始化,如果是standalone模式启动,则跳过。如果是集群模式启动,则使用线程池校验个节点时间和数据。

UpgradeJudgement  判断是否正在进行升级流程,如果是则初始化升级检查订阅检查升级。

ServerStatusManager  构造对象并注册Server状态更新对象,当本地Server发生变化时,更新server状态

SwitchManager  初始化并注册监听SwitchDomain 变更

ServerListManager   节点列表管理器初始化,订阅节点信息,订阅成员信息上报和更新。


2.1.5.5 config模块

CapacityService   初始化容量管理服务,本意是要限制创建的命名空间数量和配置数量,实际上目前并没有启用限制。

ConnectionManager 连接管理器,构造函数中注册监听连接限制规则变更事件。初始化先从本地加载 data/loader/limitRule ,如果目录不存在则创建。并注册监听 data/loader目录,如果 data/loader/limitRule文件发生变化,则重新载入文件内容。

 

DumpService 构造函数,这里依赖persistService 来持久化到数据库,依赖ServerMemberManager来获取节点变更情况,将构造的对象作为参数,创建了4Processor。这些DumpProcessor的主要作用是将更新配置、灰度、tag等数据持久化到磁盘。然后创建两个通用的TaskManager, 将前面创建的Processor作为参数传入,由Task管理Processor的执行。最后获取数据源,根据Condition条件,初始化Jdbc,将初始化状态设为true.

 

EmbeddedDumpService  构造函数创建 EmbeddedDumpService对象,初始化函数执行流程如下:

getCpProtocol中初始化Jraft Executer,其中各个Executor的线程数默认依次为 raftCoreExecutor  4raftCliServiceExecutor 4raftCommonExecutor  8raftSnapshotExecutor 4

protocolManager.getCpProtocol(),默认是CP模式启动,这里会初始化协议,通过MemberManager将节点信息注入到协议中,构造JRaftServerJRaftProtocol对象并初始化,其中包括创建raft executer,加载raft相关配置,创建raft节点和group,详细内容见Raft初始化章节,最后启动rpcServer

raftServer启动成功,但此时还需要等待创建group和选举成功后才能执行dump操作。

   @PostConstruct
    @Override
    protected void init() throws Throwable {
		//如果是standalone模式启动,直接执行dump操作然后返回
        if (EnvUtil.getStandaloneMode()) {
            dumpOperate(processor, dumpAllProcessor, dumpAllBetaProcessor, dumpAllTagProcessor);
            return;
        }
        //默认是CP模式启动,这里会初始化协议,通过MemberManager将节点信息注入到协议中,构造JRaftServer和JRaftProtocol对象并初始化,其中包括创建raft  executer,加载raft相关配置,创建raft节点和group,详细内容见Raft初始化章节,最后启动rpcServer
       CPProtocol protocol = protocolManager.getCpProtocol();
        AtomicReference<Throwable> errorReference = new AtomicReference<>(null);
        CountDownLatch waitDumpFinish = new CountDownLatch(1);
        
        // watch path => /nacos_config/leader/ has value ?
        Observer observer = new Observer() {
            
            @Override
            public void update(Observable o) {
                if (!(o instanceof ProtocolMetaData.ValueItem)) {
                    return;
                }
                final Object arg = ((ProtocolMetaData.ValueItem) o).getData();
                GlobalExecutor.executeByCommon(() -> {
                    // must make sure that there is a value here to perform the correct operation that follows
                    if (Objects.isNull(arg)) {
                        return;
                    }
                    // Identify without a timeout mechanism
                    EmbeddedStorageContextUtils.putExtendInfo(Constants.EXTEND_NEED_READ_UNTIL_HAVE_DATA, "true");
                    // Remove your own listening to avoid task accumulation
                    boolean canEnd = false;
                    for (; ; ) {
                        try {
							// 从leaderdump数据到本地
                            dumpOperate(processor, dumpAllProcessor, dumpAllBetaProcessor, dumpAllTagProcessor);
                            //
                            protocol.protocolMetaData()
                                    .unSubscribe(Constants.CONFIG_MODEL_RAFT_GROUP, MetadataKey.LEADER_META_DATA, this);
                            canEnd = true;
                        } catch (Throwable ex) {
                            if (!shouldRetry(ex)) {
                                errorReference.set(ex);
                                canEnd = true;
                            }
                        }
                        if (canEnd) {
                            ThreadUtils.countDown(waitDumpFinish);
                            break;
                        }
                        ThreadUtils.sleep(500L);
                    }
                    EmbeddedStorageContextUtils.cleanAllContext();
                });
            }
        };
        //observer订阅nacos_config   leader metadata 
        protocol.protocolMetaData()
                .subscribe(Constants.CONFIG_MODEL_RAFT_GROUP, MetadataKey.LEADER_META_DATA, observer);
        
        //这里必须要等待dump任务完成回调,才能继续进行初始化
        ThreadUtils.latchAwait(waitDumpFinish);
        
        // If an exception occurs during the execution of the dump task, the exception
        // needs to be thrown, triggering the node to start the failed process
        final Throwable ex = errorReference.get();
        if (Objects.nonNull(ex)) {
            throw ex;
        }
    }

PersistentClientOperationServiceImpl   初始化,调用 raftServer createMultiRaftGroup方法创建naming_persistent_service_v2 ,创建group的详细流程见raft初始化

InstanceMetadataProcessor            初始话,,调用 raftServer createMultiRaftGroup方法创建naming_instance_metadata,创建group的详细流程见raft初始化

ServiceMetadataProcessor  构造,调用 raftServer createMultiRaftGroup方法创建naming_service_metadata,创建group的详细流程见3.3节。

2.1.5.6 plugin-default-impl模块

JwtTokenManager  初始化,确定认证方式和认证相关配置信息。

2.1.6 afterRefresh

说明ApplicationContext初始化Bean已完成,启动Tomcat, 绑定端口启动成功

2.1.7 started 阶段

关闭启动过程中的定时任务。
        设置启动状态为true,表示已启动。
       打印启动模式和存储模式。

2.1.7 ready阶段

启动已完成。


本篇主要记录了Nacos 启动执行初始化的主要流程,下一篇介绍Nacos启动过程中非常重要的raft协议的初始化过程。

文章来自个人专栏
个人技术随笔
1 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0