最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

前要

本文以攜程的Apollo和阿里的Nacos為例。

pom中引入一下依賴(lài):

        <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)程加載配置都是通過(guò)ConfigurableEnvironmentPropertySource完成的,步驟如下:

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

至于這個(gè)過(guò)程是怎么觸發(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 ,該類(lèi)為一個(gè)BeanFactoryPostProcessor,同時(shí)為了獲取ConfigurableEnvironment,該類(lèi)實(shí)現(xiàn)了EnvironmentAware回調(diào)接口。該類(lèi)何時(shí)被加入spring容器?是通過(guò)@EnableApolloConfig@Import注解的類(lèi)ApolloConfigRegistrar來(lái)加入,常規(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)聽(tīng)器
    	// 監(jiān)聽(tīng)器監(jiān)聽(tīng)到ConfigChangeEvent后publish一個(gè)ApolloConfigChangeEvent
    	// 等于將apollo自定義的ConfigChangeEvent事件機(jī)制轉(zhuǎn)化為了spring的ApolloConfigChangeEvent事件
    	initializeAutoUpdatePropertiesFeature(beanFactory);
  	}
	private void initializePropertySources() {
		// 聚合類(lèi),該類(lèi)也是一個(gè)PropertySource,代理了一堆PropertySource
		// 該類(lèi)中有一個(gè) Set<PropertySource<?>> 字段
		CompositePropertySource composite = new ...;
		...
		// 從 遠(yuǎn)程 或 本地緩存 獲取配置
		Config config = ConfigService.getConfig(namespace);
		// 適配Config到PropertySource,并加入聚合類(lèi)		
		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)聽(tīng)器,監(jiān)聽(tīng)器監(jiān)聽(tīng)到ConfigChangeEvent后發(fā)布ApolloConfigChangeEvent
    	ConfigChangeListener configChangeEventPublisher = changeEvent ->
        	applicationEventPublisher.publishEvent(new ApolloConfigChangeEvent(changeEvent));
		// 注冊(cè)監(jiān)聽(tīng)器到每個(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)行更新。

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

public class RemoteConfigRepository extends AbstractConfigRepository implements ConfigRepository{
  public RemoteConfigRepository(String namespace) {
  	...
  	// 定時(shí)拉取
	this.schedulePeriodicRefresh();
	// 長(zhǎng)輪詢(xún)
	this.scheduleLongPollingRefresh();
	...
  }
  private void schedulePeriodicRefresh() {
    // 定時(shí)線程池
    m_executorService.scheduleAtFixedRate(
        new Runnable() {
          @Override
          public void run() {
          	// 調(diào)用父抽象類(lèi)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ā)布事件,該方法在父抽象類(lèi)中
        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í)刷新和長(zhǎng)輪詢(xún)刷新。這兩個(gè)功能在 apollo 的 github 文檔中也有介紹:

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

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

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)類(lèi)實(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)類(lèi)
		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做分類(lèi)
				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)程獲取配置都是通過(guò)向ConfigurableEnvironment插入從遠(yuǎn)程獲取的數(shù)據(jù)轉(zhuǎn)化的PropertySource。而從遠(yuǎn)程獲取就涉及到長(zhǎng)輪詢(xú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)文章

  • Java Objects工具類(lèi)原理及用法詳解

    Java Objects工具類(lèi)原理及用法詳解

    這篇文章主要介紹了Java Objects工具類(lèi)原理及用法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • java 查找字符串所在的位置代碼

    java 查找字符串所在的位置代碼

    這篇文章主要介紹了java 查找字符串所在的位置代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • java+mysql模擬實(shí)現(xiàn)銀行系統(tǒng)

    java+mysql模擬實(shí)現(xiàn)銀行系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java+mysql模擬實(shí)現(xiàn)銀行系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • 學(xué)習(xí)JVM之java內(nèi)存區(qū)域與異常

    學(xué)習(xí)JVM之java內(nèi)存區(qū)域與異常

    關(guān)于JVM內(nèi)存區(qū)域的知識(shí)對(duì)于初學(xué)者來(lái)說(shuō)其實(shí)是很重要的,了解Java內(nèi)存分配的原理,這對(duì)于以后JAVA的學(xué)習(xí)會(huì)有更深刻的理解。下面來(lái)看看詳細(xì)介紹。
    2016-07-07
  • Java基礎(chǔ)--反射機(jī)制

    Java基礎(chǔ)--反射機(jī)制

    本文主要介紹了Java反射機(jī)制的相關(guān)知識(shí)。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-03-03
  • ArrayList和LinkedList區(qū)別及使用場(chǎng)景代碼解析

    ArrayList和LinkedList區(qū)別及使用場(chǎng)景代碼解析

    這篇文章主要介紹了ArrayList和LinkedList區(qū)別及使用場(chǎng)景代碼解析,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • Mybatis中使用in()查詢(xún)的方式詳解

    Mybatis中使用in()查詢(xún)的方式詳解

    當(dāng)參數(shù)有值,添加條件查詢(xún),附帶一個(gè)字符串的in查詢(xún),下面這篇文章主要給大家介紹了關(guān)于Mybatis中使用in()查詢(xún)的方式,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • MyBatis還是JPA?終于有答案了

    MyBatis還是JPA?終于有答案了

    這篇文章主要介紹了MyBatis還是JPA,中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • 單點(diǎn)登錄的概念及SpringBoot實(shí)現(xiàn)單點(diǎn)登錄的操作方法

    單點(diǎn)登錄的概念及SpringBoot實(shí)現(xiàn)單點(diǎn)登錄的操作方法

    在本文中,我們將使用Spring Boot構(gòu)建一個(gè)基本的單點(diǎn)登錄系統(tǒng),我們將介紹如何使用Spring Security和JSON Web Tokens(JWTs)來(lái)實(shí)現(xiàn)單點(diǎn)登錄功能,本文假設(shè)您已經(jīng)熟悉Spring Boot和Spring Security,感興趣的朋友一起看看吧
    2024-10-10
  • Java面向?qū)ο笾?lèi)的繼承介紹

    Java面向?qū)ο笾?lèi)的繼承介紹

    大家好,本篇文章主要講的是Java面向?qū)ο笾?lèi)的繼承介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-02-02

最新評(píng)論

瓮安县| 靖江市| 溧水县| 福安市| 瑞金市| 海林市| 巩留县| 四川省| 潼南县| 绩溪县| 晋江市| 云梦县| 乐东| 惠州市| 岐山县| 青海省| 苏尼特左旗| 互助| 庆阳市| 连云港市| 吴旗县| 杨浦区| 新建县| 衢州市| 海淀区| 宁国市| 新乐市| 崇礼县| 庆安县| 庆云县| 锦屏县| 新龙县| 巴塘县| 穆棱市| 攀枝花市| 南充市| 阿拉善盟| 临沭县| 明溪县| 珠海市| 桦南县|