cloud Alibaba Nacos配置中心源码(1) bootstrap初始化

     

目录

1、NacosConfigBootstrapConfiguration

1.1 NacosConfigManager

1.1.1  Nacos Agent 验证serverAddr

1.1.2 GetServerListTask 更新endpoint

1.1.3  ClientWorker轮询配置文件变化

总结:


        老规矩,springboot 组件,先看spring.factories。

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.NacosConfigAutoConfiguration,\
com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration

1、NacosConfigBootstrapConfiguration

        bootstrapContext是主applicationContext的父上下文,它优先于applicationContext创建。

它共初始化了三个bean,第一个是NacosConfigProperties。第二个是NacosConfigManager,第三个是nacosPropertySourceLocator。

1.1 NacosConfigManager

它的构造方法,主要就是调用创建configService。

static ConfigService createConfigService(
			NacosConfigProperties nacosConfigProperties) {
        //并发加锁,防止多次创建
		if (Objects.isNull(service)) {
			synchronized (NacosConfigManager.class) {
				try {
					if (Objects.isNull(service)) {
						service = NacosFactory.createConfigService(
                                //将配置的一些关键属性转换做参数
								nacosConfigProperties.assembleConfigServiceProperties());
					}
				}
				catch (NacosException e) {
					log.error(e.getMessage());
					throw new NacosConnectionFailureException(
							nacosConfigProperties.getServerAddr(), e.getMessage(), e);
				}
			}
		}
		return service;
	}

 跟踪NacosFactory.createConfigService代码,调用

public static ConfigService createConfigService(Properties properties) throws NacosException {
        try {
            //反射创建ConfigService,我们直接看创建类的构造方法
            Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService");
            Constructor constructor = driverImplClass.getConstructor(Properties.class);
            ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties);
            return vendorImpl;
        } catch (Throwable e) {
            throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
        }
    }
public NacosConfigService(Properties properties) throws NacosException {
        ValidatorUtils.checkInitParam(properties);
        //编码格式
        String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE);
        if (StringUtils.isBlank(encodeTmp)) {
            //默认utf-8
            this.encode = Constants.ENCODE;
        } else {
            this.encode = encodeTmp.trim();
        }
        //重新初始化 namespace,如果是阿里云环境,并配置了从云环境获取namespace那么就取云环境的, 
         //没有则取配置文件的,配置文件没有则空
        initNamespace(properties);
        //①重点,初始化agent
        this.agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
        ②
        this.agent.start();
        //③启动一个线程组 对比配置文件变化的,10毫秒
        this.worker = new ClientWorker(this.agent, this.configFilterChainManager, properties);
    }

1.1.1  Nacos Agent 验证serverAddr

         先看看ServerHttpAgent的构造方法:

public ServerHttpAgent(Properties properties) throws NacosException {
        //当配置了serverAddr 直接解析它组成serviceUrl 集合
        //没配serverAddr则取endPoint,组成获取serviceUrl的链接
        //两个属性都没配则会报 endpoint is blank
        this.serverListMgr = new ServerListManager(properties);
        //登录用的,目前我的server是走的默认用户名密码,因为nacos将只是内网可访问。当没配    
         //username时,后面的登录也将不验证
        this.securityProxy = new SecurityProxy(properties, NACOS_RESTTEMPLATE);
        this.namespaceId = properties.getProperty(PropertyKeyConst.NAMESPACE);
        //maxRetry,编码等属性初始化
        init(properties);
        //nacos server或者用户是否有效,其中包含token有效期内不会重复发http
        this.securityProxy.login(this.serverListMgr.getServerUrls());
        
        // 构建定时线程池,并给它的线程赋名字和守护线程属性,核心线程1,最大int.max
        this.executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                t.setName("com.alibaba.nacos.client.config.security.updater");
                t.setDaemon(true);
                return t;
            }
        });
        //5秒 定时间隔执行,验证nacos server 是否在线或者配置的用户是否正确
        this.executorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                securityProxy.login(serverListMgr.getServerUrls());
            }
        }, 0, this.securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS);
        
    }

        ServerHttpAgent就是包装一些http请求,并加工nacos server的服务端地址,并启动一个定时任务不断验证server是否在线。MetricsHttpAgent通过包装它对外提供服务。

1.1.2 GetServerListTask 更新endpoint

        在构造configService时,在开始构造了一个agent后,还会调用start方法,start方法调用的是serverListManager的start。

public synchronized void start() throws NacosException {
        
        if (isStarted || isFixed) {
            return;
        }
        //获取serverlist线程
        GetServerListTask getServersTask = new GetServerListTask(addressServerUrl);
        //注意这里的条件,serverUrls为空才会run,而serverUrls来源于serverAddr配置,也就是如果 
         //我们配置了serverAddr不是用的endpoint模式获取nacos server,这个线程也不会运行
        for (int i = 0; i < initServerlistRetryTimes && serverUrls.isEmpty(); ++i) {
            getServersTask.run();
            try {
                this.wait((i + 1) * 100L);
            } catch (Exception e) {
                LOGGER.warn("get serverlist fail,url: {}", addressServerUrl);
            }
        }
        
        if (serverUrls.isEmpty()) {
            LOGGER.error("[init-serverlist] fail to get NACOS-server serverlist! env: {}, url: {}", name,
                    addressServerUrl);
            throw new NacosException(NacosException.SERVER_ERROR,
                    "fail to get NACOS-server serverlist! env:" + name + ", not connnect url:" + addressServerUrl);
        }
        
        //线程池运行线程,30秒运行检查一次 server 地址变更
        this.executorService.scheduleWithFixedDelay(getServersTask, 0L, 30L, TimeUnit.SECONDS);
        isStarted = true;
    }

        

        run方法就是执行了http方法从endpoint查询nacos server是否变更,如果变更将更新当前属性serverUrls。 

1.1.3  ClientWorker轮询配置文件变化

public ClientWorker(final HttpAgent agent, final ConfigFilterChainManager configFilterChainManager,
            final Properties properties) {
        //就是上面说的MetricsHttpAgent 包装类
        this.agent = agent;
        this.configFilterChainManager = configFilterChainManager;
        
        // Initialize the timeout parameter
        init(properties);
        
        //checkConfigInfo的线程池
        this.executor = Executors.newScheduledThreadPool(1, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                t.setName("com.alibaba.nacos.client.Worker." + agent.getName());
                t.setDaemon(true);
                return t;
            }
        });
        //线程数:cpu核心线程数
        this.executorService = Executors
                .newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
                    @Override
                    public Thread newThread(Runnable r) {
                        Thread t = new Thread(r);
                        t.setName("com.alibaba.nacos.client.Worker.longPolling." + agent.getName());
                        t.setDaemon(true);
                        return t;
                    }
                });
        //每10毫秒检查一次config配置
        this.executor.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    checkConfigInfo();
                } catch (Throwable e) {
                    LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e);
                }
            }
        }, 1L, 10L, TimeUnit.MILLISECONDS);
    }

重点就在checkConfigInfo内的逻辑

public void checkConfigInfo() {
        // Dispatch taskes.
        int listenerSize = cacheMap.size();
        // Round up the longingTaskCount.
        int longingTaskCount = (int) Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize());
        if (longingTaskCount > currentLongingTaskCount) {
            for (int i = (int) currentLongingTaskCount; i < longingTaskCount; i++) {
                // The task list is no order.So it maybe has issues when changing.
                executorService.execute(new LongPollingRunnable(i));
            }
            currentLongingTaskCount = longingTaskCount;
        }
    }

       因为是异步任务,当初始化完ClientWorker时,cacheMap是空的,因此是不会往下执行多线程任务的,那么这个cacheMap是什么时候存放进去数据的呢,我们可以在当前类查看这个cacheMap它在哪里都被引用了,它是一个ConcrrentHashMap,哪些方法是put数据的。源码太多,画了个简易流程图

        

    

总结:

1、当配置serverAddr时:启动线程每5秒查看server是否在线

     当配置endpoint时:每30秒启动线程从endpoint中更新server地址

2、从server里查出配置信息,按dataId+groupId 在本地文件系统存储 snap文件

3、每10毫秒启动线程对比 clientWorker中的cacheMap内容 和拉取 server 内容进行对比,使用MD5对比。MD5加密的内容就是文件内容。

4、启动监听器,监听ApplicationReadyEvent事件,启动完成后,会从本地文件将内容加载出来,存放到clientWorker的cacheMap内。

        

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值