文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring远程加载配置如何实现

2023-07-05 17:04

关注

本篇内容主要讲解“Spring远程加载配置如何实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring远程加载配置如何实现”吧!

前要

本文以携程的Apollo和阿里的Nacos为例。

pom中引入一下依赖:

        <dependency>            <groupId>com.ctrip.framework.apollo</groupId>            <artifactId>apollo-client</artifactId>            <version>2.0.1</version>        </dependency>        <dependency>            <groupId>com.alibaba.cloud</groupId>            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>            <version>2021.1</version>        </dependency>

不管是Apollo还是Nacos,实现从远程加载配置都是通过ConfigurableEnvironmentPropertySource完成的,步骤如下:

至于这个过程是怎么触发和运行的,要看具体实现。

Apollo

关注PropertySourcesProcessor ,该类为一个BeanFactoryPostProcessor,同时为了获取ConfigurableEnvironment,该类实现了EnvironmentAware回调接口。该类何时被加入spring容器?是通过@EnableApolloConfig@Import注解的类ApolloConfigRegistrar来加入,常规套路。

public class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware,    ApplicationEventPublisherAware, PriorityOrdered {// aware回调接口设置private ConfigurableEnvironment environment;@Override  public void setEnvironment(Environment environment) {    //it is safe enough to cast as all known environment is derived from ConfigurableEnvironment    this.environment = (ConfigurableEnvironment) environment;  }@Override  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {  // 获取配置    this.configUtil = ApolloInjector.getInstance(ConfigUtil.class);    // 从远程获取PropertySource    initializePropertySources();    // 为每个ConfigPropertySource注册ConfigChangeEvent监听器    // 监听器监听到ConfigChangeEvent后publish一个ApolloConfigChangeEvent    // 等于将apollo自定义的ConfigChangeEvent事件机制转化为了spring的ApolloConfigChangeEvent事件    initializeAutoUpdatePropertiesFeature(beanFactory);  }private void initializePropertySources() {// 聚合类,该类也是一个PropertySource,代理了一堆PropertySource// 该类中有一个 Set<PropertySource<?>> 字段CompositePropertySource composite = new ...;...// 从 远程 或 本地缓存 获取配置Config config = ConfigService.getConfig(namespace);// 适配Config到PropertySource,并加入聚合类composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));// 添加到ConfigurableEnvironmentenvironment.getPropertySources().addFirst(composite);} private void initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory beanFactory) {    if (!AUTO_UPDATE_INITIALIZED_BEAN_FACTORIES.add(beanFactory)) {      return;    }// 定义监听器,监听器监听到ConfigChangeEvent后发布ApolloConfigChangeEvent    ConfigChangeListener configChangeEventPublisher = changeEvent ->        applicationEventPublisher.publishEvent(new ApolloConfigChangeEvent(changeEvent));// 注册监听器到每个PropertySource    List<ConfigPropertySource> configPropertySources = configPropertySourceFactory.getAllConfigPropertySources();    for (ConfigPropertySource configPropertySource : configPropertySources) {      configPropertySource.addChangeListener(configChangeEventPublisher);   }  }...}

从上面可知初始化时会从ConfigService远程拉取配置,并保存到内部缓存。而后续远程配置中心配置发生变化时本地会拉去最新配置并发布事件,PropertySource根据事件进行更新。

无论是开始从远程拉取配置初始化,还是后续远程配置更新,最终都是通过RemoteConfigRepository以http形式定时获取配置:

public class RemoteConfigRepository extends AbstractConfigRepository implements ConfigRepository{  public RemoteConfigRepository(String namespace) {  ...  // 定时拉取this.schedulePeriodicRefresh();// 长轮询this.scheduleLongPollingRefresh();...  }  private void schedulePeriodicRefresh() {    // 定时线程池    m_executorService.scheduleAtFixedRate(        new Runnable() {          @Override          public void run() {          // 调用父抽象类trySync()          // trySync()调用模版方法sync()            trySync();          }        }, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),        m_configUtil.getRefreshIntervalTimeUnit());  }  @Override  protected synchronized void sync() {  // 事务    Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");    try {      ApolloConfig previous = m_configCache.get();      // http远程拉取配置      ApolloConfig current = loadApolloConfig();      // reference equals means HTTP 304      if (previous != current) {        logger.debug("Remote Config refreshed!");        // 设置缓存        m_configCache.set(current);        // 发布事件,该方法在父抽象类中        this.fireRepositoryChange(m_namespace, this.getConfig());      }      if (current != null) {        Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()),            current.getReleaseKey());      }      transaction.setStatus(Transaction.SUCCESS);    } catch (Throwable ex) {      transaction.setStatus(ex);      throw ex;    } finally {      transaction.complete();    }    ...  }

可以看到,在构造方法中,就执行了 3 个本地方法,其中就包括定时刷新和长轮询刷新。这两个功能在 apollo 的 github 文档中也有介绍:

所以,长连接是更新配置的主要手段,然后用定时任务辅助长连接,防止长连接失败。

org.springframework.cloud.bootstrap.config

nacos实现了spring cloud config规范,规范代码的maven坐标如下:

    <dependency>      <groupId>org.springframework.cloud</groupId>      <artifactId>spring-cloud-context</artifactId>      <version>...</version>      <scope>compile</scope>    </dependency>

这里介绍规范内容,nacos的实现略。

PropertySource

PropertySource用于存储k-v键值对,远程或本地的配置最终都转化为PropertySource,放入ConfigurableEnvironment中,通常EnumerablePropertySource中会代理一个PropertySource的list。

Spring远程加载配置如何实现

PropertySourceLocator

规范接口主要为PropertySourceLocator接口,该接口用于定位PropertySource,注释如下:

Strategy for locating (possibly remote) property sources for the Environment. Implementations should not fail unless they intend to prevent the application from starting.

public interface PropertySourceLocator {// 实现类实现该方法PropertySource<?> locate(Environment environment);default Collection<PropertySource<?>> locateCollection(Environment environment) {return locateCollection(this, environment);}static Collection<PropertySource<?>> locateCollection(PropertySourceLocator locator, Environment environment) {// 调用实现类PropertySource<?> propertySource = locator.locate(environment);if (propertySource == null) {return Collections.emptyList();}// 如果该PropertySource是代理了list的CompositePropertySource,提取全部if (CompositePropertySource.class.isInstance(propertySource)) {Collection<PropertySource<?>> sources = ((CompositePropertySource) propertySource).getPropertySources();List<PropertySource<?>> filteredSources = new ArrayList<>();for (PropertySource<?> p : sources) {if (p != null) {filteredSources.add(p);}}return filteredSources;}else {return Arrays.asList(propertySource);}}}

PropertySourceBootstrapConfiguration

调用PropertySourceLocator接口将PropertySource加入ConfigurableEnvironment中。

@Configuration(proxyBeanMethods = false)@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)public class PropertySourceBootstrapConfigurationimplements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {@Autowired(required = false)private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();public void setPropertySourceLocators(Collection<PropertySourceLocator> propertySourceLocators) {this.propertySourceLocators = new ArrayList<>(propertySourceLocators);}@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {List<PropertySource<?>> composite = new ArrayList<>();// 排序AnnotationAwareOrderComparator.sort(this.propertySourceLocators);boolean empty = true;// applicationContext由回调接口提供ConfigurableEnvironment environment = applicationContext.getEnvironment();for (PropertySourceLocator locator : this.propertySourceLocators) {// 调用PropertySourceLocatorCollection<PropertySource<?>> source = locator.locateCollection(environment);...for (PropertySource<?> p : source) {// 是否代理了PropertySource的list做分类if (p instanceof EnumerablePropertySource) {EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) p;sourceList.add(new BootstrapPropertySource<>(enumerable));}else {sourceList.add(new SimpleBootstrapPropertySource(p));}}composite.addAll(sourceList);empty = false;}if (!empty) {// 获取 ConfigurableEnvironment中的MutablePropertySourcesMutablePropertySources propertySources = environment.getPropertySources();...// 执行插入到ConfigurableEnvironment的MutablePropertySourcesinsertPropertySources(propertySources, composite);...}}}

到此,相信大家对“Spring远程加载配置如何实现”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯