91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring遠程加載配置如何實現

發布時間:2023-03-27 14:12:58 來源:億速云 閱讀:127 作者:iii 欄目:開發技術

本篇內容主要講解“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完成的,步驟如下:

  • 遠程拉取配置,生成PropertySource

  • ConfigurableEnvironment獲取聚合類 MutablePropertySources propertySources = ConfigurableEnvironment#getPropertySources();

  • 將拉取的PropertySource添加到從ConfigurableEnvironment獲取的聚合類MutablePropertySources#add...(PropertySource<?> propertySource)

至于這個過程是怎么觸發和運行的,要看具體實現。

  • 在apollo-client中,使用BeanFactoryPostProcessor。

  • 在spring-cloud-starter-alibaba-nacos-config中,由于 cloud-nacos實現了spring cloud config規范(處于org.springframework.cloud.bootstrap.config包下),nacos實現該規范即可,即實現spring cloud 的PropertySourceLocator接口。

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));
		// 添加到ConfigurableEnvironment
		environment.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 文檔中也有介紹:

  • 客戶端和服務端保持了一個長連接,從而能第一時間獲得配置更新的推送。

  • 客戶端還會定時從Apollo配置中心服務端拉取應用的最新配置。

  • 這是一個fallback機制,為了防止推送機制失效導致配置不更新。

  • 客戶端定時拉取會上報本地版本,所以一般情況下,對于定時拉取的操作,服務端都會返回304 - Not Modified。

  • 定時頻率默認為每5分鐘拉取一次,客戶端也可以通過在運行時指定System Property: apollo.refreshInterval來覆蓋,單位為分鐘。

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

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 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由回調接口提供
		ConfigurableEnvironment environment = applicationContext.getEnvironment();
		for (PropertySourceLocator locator : this.propertySourceLocators) {
			// 調用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();
			...
			// 執行插入到ConfigurableEnvironment的MutablePropertySources
			insertPropertySources(propertySources, composite);
			...
		}
	}
}

到此,相信大家對“Spring遠程加載配置如何實現”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

华宁县| 广河县| 东台市| 南开区| 宁明县| 阳曲县| 嘉兴市| 枣庄市| 定陶县| 于都县| 冀州市| 图片| 仁布县| 新田县| 临猗县| 米易县| 双鸭山市| 广西| 东宁县| 工布江达县| 肃宁县| 大余县| 铜梁县| 盐池县| 宝山区| 镇江市| 通城县| 白河县| 治多县| 道真| 吴忠市| 图片| 永寿县| 江口县| 奇台县| 昭通市| 十堰市| 集贤县| 门源| 满城县| 个旧市|