欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring遠(yuǎn)程加載配置的實(shí)現(xiàn)方法詳解

 更新時(shí)間:2023年03月27日 11:23:42   作者:T.Y.Bao  
這篇文章主要介紹了Spring遠(yuǎn)程加載配置的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧

前要

本文以攜程的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,實(shí)現(xiàn)從遠(yuǎn)程加載配置都是通過ConfigurableEnvironmentPropertySource完成的,步驟如下:

  • 遠(yuǎn)程拉取配置,生成PropertySource
  • ConfigurableEnvironment獲取聚合類 MutablePropertySources propertySources = ConfigurableEnvironment#getPropertySources();
  • 將拉取的PropertySource添加到從ConfigurableEnvironment獲取的聚合類MutablePropertySources#add...(PropertySource<?> propertySource)

至于這個(gè)過程是怎么觸發(fā)和運(yùn)行的,要看具體實(shí)現(xiàn)。

  • 在apollo-client中,使用BeanFactoryPostProcessor。
  • 在spring-cloud-starter-alibaba-nacos-config中,由于 cloud-nacos實(shí)現(xiàn)了spring cloud config規(guī)范(處于org.springframework.cloud.bootstrap.config包下),nacos實(shí)現(xiàn)該規(guī)范即可,即實(shí)現(xiàn)spring cloud 的PropertySourceLocator接口。

Apollo

關(guān)注PropertySourcesProcessor ,該類為一個(gè)BeanFactoryPostProcessor,同時(shí)為了獲取ConfigurableEnvironment,該類實(shí)現(xiàn)了EnvironmentAware回調(diào)接口。該類何時(shí)被加入spring容器?是通過@EnableApolloConfig@Import注解的類ApolloConfigRegistrar來加入,常規(guī)套路。

public class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware,
    ApplicationEventPublisherAware, PriorityOrdered {
	// aware回調(diào)接口設(shè)置
	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);
    	// 從遠(yuǎn)程獲取PropertySource
    	initializePropertySources();
    	// 為每個(gè)ConfigPropertySource注冊(cè)ConfigChangeEvent監(jiān)聽器
    	// 監(jiān)聽器監(jiān)聽到ConfigChangeEvent后publish一個(gè)ApolloConfigChangeEvent
    	// 等于將apollo自定義的ConfigChangeEvent事件機(jī)制轉(zhuǎn)化為了spring的ApolloConfigChangeEvent事件
    	initializeAutoUpdatePropertiesFeature(beanFactory);
  	}
	private void initializePropertySources() {
		// 聚合類,該類也是一個(gè)PropertySource,代理了一堆PropertySource
		// 該類中有一個(gè) Set<PropertySource<?>> 字段
		CompositePropertySource composite = new ...;
		...
		// 從 遠(yuǎn)程 或 本地緩存 獲取配置
		Config config = ConfigService.getConfig(namespace);
		// 適配Config到PropertySource,并加入聚合類		
		composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
		// 添加到ConfigurableEnvironment
		environment.getPropertySources().addFirst(composite);
	}
 	private void initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory beanFactory) {
    	if (!AUTO_UPDATE_INITIALIZED_BEAN_FACTORIES.add(beanFactory)) {
      		return;
    	}
		// 定義監(jiān)聽器,監(jiān)聽器監(jiān)聽到ConfigChangeEvent后發(fā)布ApolloConfigChangeEvent
    	ConfigChangeListener configChangeEventPublisher = changeEvent ->
        	applicationEventPublisher.publishEvent(new ApolloConfigChangeEvent(changeEvent));
		// 注冊(cè)監(jiān)聽器到每個(gè)PropertySource
    	List<ConfigPropertySource> configPropertySources = configPropertySourceFactory.getAllConfigPropertySources();
    	for (ConfigPropertySource configPropertySource : configPropertySources) {
      		configPropertySource.addChangeListener(configChangeEventPublisher);
   		}
  	}
	...
}

從上面可知初始化時(shí)會(huì)從ConfigService遠(yuǎn)程拉取配置,并保存到內(nèi)部緩存。而后續(xù)遠(yuǎn)程配置中心配置發(fā)生變化時(shí)本地會(huì)拉去最新配置并發(fā)布事件,PropertySource根據(jù)事件進(jìn)行更新。

無論是開始從遠(yuǎn)程拉取配置初始化,還是后續(xù)遠(yuǎn)程配置更新,最終都是通過RemoteConfigRepository以http形式定時(shí)獲取配置:

public class RemoteConfigRepository extends AbstractConfigRepository implements ConfigRepository{
  public RemoteConfigRepository(String namespace) {
  	...
  	// 定時(shí)拉取
	this.schedulePeriodicRefresh();
	// 長輪詢
	this.scheduleLongPollingRefresh();
	...
  }
  private void schedulePeriodicRefresh() {
    // 定時(shí)線程池
    m_executorService.scheduleAtFixedRate(
        new Runnable() {
          @Override
          public void run() {
          	// 調(diào)用父抽象類trySync()
          	// trySync()調(diào)用模版方法sync()
            trySync();
          }
        }, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),
        m_configUtil.getRefreshIntervalTimeUnit());
  }
  @Override
  protected synchronized void sync() {
  	// 事務(wù)
    Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");
    try {
      ApolloConfig previous = m_configCache.get();
      // http遠(yuǎn)程拉取配置
      ApolloConfig current = loadApolloConfig();
      // reference equals means HTTP 304
      if (previous != current) {
        logger.debug("Remote Config refreshed!");
        // 設(shè)置緩存
        m_configCache.set(current);
        // 發(fā)布事件,該方法在父抽象類中
        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();
    }
    ...
  }

可以看到,在構(gòu)造方法中,就執(zhí)行了 3 個(gè)本地方法,其中就包括定時(shí)刷新和長輪詢刷新。這兩個(gè)功能在 apollo 的 github 文檔中也有介紹:

  • 客戶端和服務(wù)端保持了一個(gè)長連接,從而能第一時(shí)間獲得配置更新的推送。
  • 客戶端還會(huì)定時(shí)從Apollo配置中心服務(wù)端拉取應(yīng)用的最新配置。
  • 這是一個(gè)fallback機(jī)制,為了防止推送機(jī)制失效導(dǎo)致配置不更新。
  • 客戶端定時(shí)拉取會(huì)上報(bào)本地版本,所以一般情況下,對(duì)于定時(shí)拉取的操作,服務(wù)端都會(huì)返回304 - Not Modified。
  • 定時(shí)頻率默認(rèn)為每5分鐘拉取一次,客戶端也可以通過在運(yùn)行時(shí)指定System Property: apollo.refreshInterval來覆蓋,單位為分鐘。

所以,長連接是更新配置的主要手段,然后用定時(shí)任務(wù)輔助長連接,防止長連接失敗。

org.springframework.cloud.bootstrap.config

nacos實(shí)現(xiàn)了spring cloud config規(guī)范,規(guī)范代碼的maven坐標(biāo)如下:

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

這里介紹規(guī)范內(nèi)容,nacos的實(shí)現(xiàn)略。

PropertySource

PropertySource用于存儲(chǔ)k-v鍵值對(duì),遠(yuǎn)程或本地的配置最終都轉(zhuǎn)化為PropertySource,放入ConfigurableEnvironment中,通常EnumerablePropertySource中會(huì)代理一個(gè)PropertySource的list。

PropertySourceLocator

規(guī)范接口主要為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 {
	// 實(shí)現(xiàn)類實(shí)現(xiàn)該方法
	PropertySource<?> locate(Environment environment);
	default Collection<PropertySource<?>> locateCollection(Environment environment) {
		return locateCollection(this, environment);
	}
	static Collection<PropertySource<?>> locateCollection(PropertySourceLocator locator, Environment environment) {
		// 調(diào)用實(shí)現(xiàn)類
		PropertySource<?> propertySource = locator.locate(environment);
		if (propertySource == null) {
			return Collections.emptyList();
		}
		// 如果該P(yáng)ropertySource是代理了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

調(diào)用PropertySourceLocator接口將PropertySource加入ConfigurableEnvironment中。

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
public class PropertySourceBootstrapConfiguration
		implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
	@Autowired(required = false)
	private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
	public void setPropertySourceLocators(Collection<PropertySourceLocator> propertySourceLocators) {
		this.propertySourceLocators = new ArrayList<>(propertySourceLocators);
	}
	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		List<PropertySource<?>> composite = new ArrayList<>();
		// 排序
		AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
		boolean empty = true;
		// applicationContext由回調(diào)接口提供
		ConfigurableEnvironment environment = applicationContext.getEnvironment();
		for (PropertySourceLocator locator : this.propertySourceLocators) {
			// 調(diào)用PropertySourceLocator
			Collection<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中的MutablePropertySources
			MutablePropertySources propertySources = environment.getPropertySources();
			...
			// 執(zhí)行插入到ConfigurableEnvironment的MutablePropertySources
			insertPropertySources(propertySources, composite);
			...
		}
	}
}

總結(jié)

可以看到從遠(yuǎn)程獲取配置都是通過向ConfigurableEnvironment插入從遠(yuǎn)程獲取的數(shù)據(jù)轉(zhuǎn)化的PropertySource。而從遠(yuǎn)程獲取就涉及到長輪詢、本地緩存等內(nèi)容,設(shè)計(jì)都比較一致。

到此這篇關(guān)于Spring遠(yuǎn)程加載配置的實(shí)現(xiàn)方法詳解的文章就介紹到這了,更多相關(guān)Spring遠(yuǎn)程加載配置內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Springboot整合SpringSecurity的完整案例詳解

    Springboot整合SpringSecurity的完整案例詳解

    Spring Security是基于Spring生態(tài)圈的,用于提供安全訪問控制解決方案的框架,Spring Security登錄認(rèn)證主要涉及兩個(gè)重要的接口 UserDetailService和UserDetails接口,本文對(duì)Springboot整合SpringSecurity過程給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2024-01-01
  • 關(guān)于IDEA報(bào)錯(cuò)Error:java 不支持發(fā)行版本17的原因及解決方案

    關(guān)于IDEA報(bào)錯(cuò)Error:java 不支持發(fā)行版本17的原因及解決方案

    在rebuild或運(yùn)行項(xiàng)目時(shí)提示“Error:java: 錯(cuò)誤: 不支持發(fā)行版本 17”,本文將給大家介紹了IDEA提示“Error:java: 錯(cuò)誤: 不支持發(fā)行版本17”的原因及解決方案,需要的朋友可以參考下
    2023-09-09
  • Java如何利用狀態(tài)模式(state pattern)替代if else

    Java如何利用狀態(tài)模式(state pattern)替代if else

    這篇文章主要給大家介紹了關(guān)于Java如何利用狀態(tài)模式(state pattern)替代if else的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • 淺析java volatitle 多線程問題

    淺析java volatitle 多線程問題

    Volatile修飾的成員變量在每次被線程訪問時(shí),都強(qiáng)迫從共享內(nèi)存中重讀該成員變量的值。而且,當(dāng)成員變量發(fā)生變化時(shí),強(qiáng)迫線程將變化值回寫到共享內(nèi)存
    2013-08-08
  • 利用Java連接Hadoop進(jìn)行編程

    利用Java連接Hadoop進(jìn)行編程

    這篇文章主要介紹了利用Java連接Hadoop進(jìn)行編程,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下
    2022-06-06
  • spring單例如何改多例

    spring單例如何改多例

    這篇文章主要介紹了spring單例如何改多例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • java利用POI讀取excel文件的方法

    java利用POI讀取excel文件的方法

    這篇文章主要介紹了java利用POI讀取excel文件的方法,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-12-12
  • SpringBoot在項(xiàng)目停止(服務(wù)停止/關(guān)閉退出)之后執(zhí)行的方法

    SpringBoot在項(xiàng)目停止(服務(wù)停止/關(guān)閉退出)之后執(zhí)行的方法

    這篇文章主要給大家介紹了SpringBoot在項(xiàng)目停止(服務(wù)停止/關(guān)閉退出)之后執(zhí)行的兩種方法,實(shí)現(xiàn)DisposableBean接口和使用@PreDestroy注解,文中有詳細(xì)的代碼講解,具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-12-12
  • MyBatis動(dòng)態(tài)SQL與緩存原理深入分析

    MyBatis動(dòng)態(tài)SQL與緩存原理深入分析

    這篇文章主要介紹了MyBatis動(dòng)態(tài)SQL與緩存原理,Mybatis框架的動(dòng)態(tài)SQL技術(shù)是一種根據(jù)特定條件動(dòng)態(tài)拼裝SQL語句的功能,它存在的意義是為了解決拼接SQL語句字符串時(shí)的痛點(diǎn)問題
    2023-02-02
  • 了解SpringMVC的上傳和下載

    了解SpringMVC的上傳和下載

    今天小編就為大家分享一篇關(guān)于Spring整合Springmvc的相關(guān)介紹,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2021-07-07

最新評(píng)論