Nacos获取配置原理

spring在run()方法启动时会调用prepareContext()准备上下文。在prepareEnvironment()方法准备好环境时会发布ApplicationEnvironmentPreparedEvent事件,BootstrapApplicationListener监听器对这个事件感兴趣,会调用onApplicationEvent()方法进行响应。此时会通过bootstrapServiceContext()来读取spring.factories中BootstrapConfiguration配置的初始化器,PropertySourceBootstrapConfiguration会被读取并加载。然后在prepareContext()中会应用PropertySourceBootstrapConfiguration类中的initialize()方法进行初始化。PropertySourceBootstrapConfiguration中的属性propertySourceLocators使用了@Autowired注解,此时会到容器中获取实现了PropertySourceLocator接口的Bean。
initialize()这个方法会先创建PropertySource类的子类CompositePropertySource实例对象,使用"bootstrapProperties"表示这个属性源的名字,当获取到外部合成的属性源就会被添加这个实例对象中set集合中。

@Autowired(required = false)
private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();

Nacos的NacosPropertySourceLocator类实现了PropertySourceLocator,而且类在NacosConfigBootstrapConfiguration中被标注了@Bean注解,因此会被注入到propertySourceLocators属性中。当PropertySourceBootstrapConfiguration类中的initialize()方法调用时即到NacosPropertySourceLocator.java类中调用locate()方法进入Nacos的配置加载流程。

public class NacosPropertySourceLocator implements PropertySourceLocator {}
@Bean
public NacosPropertySourceLocator nacosPropertySourceLocator() {
    return new NacosPropertySourceLocator();
}
  1. 调用NacosConfigProperties中的configServiceInstance()创建ConfigService实例,首先获取NacosConfigProperties中的属性,即以spring.cloud.nacos.config为开头的配置项的值。接下来使用获取到的属性以反射创建ConfigService实例。创建"NACOS"的CompositePropertySource实例,保存Nacos的配置源。
  2. 加载应用配置loadApplicationConfiguration():通过由配置项生成的dataId到Nacos服务端获取配置源loadNacosDataIfPresent()。
String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
NacosPropertySource ps = nacosPropertySourceBuilder.build(dataId, group,
        fileExtension, isRefreshable);
composite.addFirstPropertySource(ps);
  1. 从环境中获取所有的属性源,将Nacos合成的属性源添加到环境属性源集合this.propertySourceList.add(propertySource);

loadNacosDataIfPresent()中,第一次到服务器获取配置源loadNacosData(),之后就从缓存中获取配置源,最终到NacosConfigService.java类中getConfigInner()方法从服务端获取配置源。

  1. 使用dataId、group创建ConfigResponse对象保存来自服务端的响应
  2. 优先使用本地配置:使用serverName(服务名)/dataId/group/tenant(默认命名空间public)构造配置文件,然后从配置文件中读取,如果本地配置文件不存在则从Nacos服务端读取
  3. 重头戏来了,我们本地没有配置,就通过ClientWorker.java类的getServerConfig()到服务端读取配置。
    1. 将"dataId"、“group”、"tenant"这三个参数加到List集合中保存下来
    2. 设置超时时间(默认3s),向服务端的/v1/cs/configs路径发get请求
    3. 以UTF-8编码方式对参数进行编码,得到query参数
    static private String encodingParams(List<String> paramValues, String encoding)
         throws UnsupportedEncodingException {
         StringBuilder sb = new StringBuilder();
         if (null == paramValues) {
             return null;
         }
         //遍历参数集合,第一个元素为key,然后加=,第二个元素为value
         for (Iterator<String> iter = paramValues.iterator(); iter.hasNext(); ) {
             sb.append(iter.next()).append("=");
             sb.append(URLEncoder.encode(iter.next(), encoding));
             if (iter.hasNext()) {
                 sb.append("&");
             }
         }
         return sb.toString();
     }
    
    1. 获取请求url的MD5值,使用谷歌RateLimiter框架通过MD5值进行请求次数的限制,默认1秒5次请求
    2. 使用Java原生的HttpURLConnection发送请求获取响应,将响应字节流编码成字符串,至此完成从服务端获取配置源
  4. 成功地获取到来自服务端的配置信息后会被保存在本地,使用serverName(服务名)/dataId/group/tenant(默认命名空间public)/group/dataId构造配置文件,然后将获取到的配置写到本地配置文件中保存下来
public static void writeStringToFile(File file, String data, String encoding)
    throws IOException {
    OutputStream os = null;
    try {
        os = new FileOutputStream(file);
        os.write(data.getBytes(encoding));
    } finally {
        if (null != os) {
            os.close();
        }
    }
}
  1. 将配置保存到ConfigResponse实例对象中

在从服务端获取到配置源后,将配置源封装成NacosPropertySource实例,再将NacosPropertySource配置源添加到CompositePropertySource(外部合成配置源实例),之后就是添加到spring的环境中,通过@Value注解获取了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值