配置中心阿波羅學習之二 項目實戰

一、背景

通過上一篇日誌,我們已經將一個單機版的apollo server端搭建起來了,並且可以通過官方提供的demo實現最基本的配置文件的讀取。接下來我主要通過一個實際的spring項目來演示如何利用apollo的java客戶端實現項目的配置文件動態更新。

apollo的Java客戶端可以通過純java API,spring註解,springboot註解等形式綁定到我們的項目中,我項目使用的spring4.x搭建的,因此我使用spring註解方式將apollo客戶端添加到我的工程中來。

二、實戰

1.先引入apollo-client的maven依賴

            <dependency>
		        <groupId>com.ctrip.framework.apollo</groupId>
		        <artifactId>apollo-client</artifactId>
		        <version>1.1.0</version>
		    </dependency>

2.創建自定義namespace,其中application是默認的namespace(一個namespace可以理解成一個properties文件的名稱,因爲我只用到了properties類型的配置文件)

創建好對應的namespace之後,頁面會提示需要發佈對應的key-value配置項,如果爲空的話會導致Apollo client所在的工程報空指針異常!

3. 工程如何引入在Apollo中的各配置項?

namespace配置文件定位
apollo利用appid、cluster_name、namespace來最終定位一個配置文件,
其中 appid和cluster_name都是作爲系統參數在應用啓動之前就設置好了,而namespace作爲客戶端程序初始化的一個參數設定!

appid通過在項目的META-INF下面的app.prperties文件中指定,例:app.id=yanghe66966,其中yanghe66966爲apollo中創建的一個appid,如何創建官網上面寫的很詳細,我這裏就不羅嗦了

cluster_name通過jvm運行參數指定:“-Dapollo.cluster=default”

  • Apollo首先會嘗試從用戶指定的cluster名稱的集羣加載配置
  • 如果沒找到,會從默認的集羣(default)加載

namespace的獲取,在javaconfig中,使用@EnableApolloConfig()註解,加載指定的namespace

@Configuration()
@EnableApolloConfig(value = {"application","TEST2.RegistryCenter","TEST2.MessageMiddleware","TEST2.DistributedCaching"}, order = 10)//appllo
public class BasicConfig {
	{
		/**
		 * 集羣和環境設置未起作用
		 */
		System.setProperty("apollo.meta", "http://192.168.219.130:8080");
		System.setProperty("idc", "DEV");//環境
	}
	
	@Bean
    public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
		
        return new PropertySourcesPlaceholderConfigurer();
    }
}

創建與四個namespace一一對應的四個本地POJO對象,目的是將指定的配置文件封裝到指定對象,便於整個工程調用

如RegistryCenterProperties類的代碼如下,@component註解表示當前類的生命週期由spring負責,@value()註解就是使用的spring自帶的註解

/**
 * 註冊中心配置項
 * @Description: 
 * @author  yangcheng
 * @date:   2019年6月23日
 */
@Component
public class RegistryCenterProperties {
	Logger logger = LoggerFactory.getLogger(RegistryCenterProperties.class);
	private final String curNamespace = "TEST2.RegistryCenter";
	/**
	 * 是否開啓集羣模式
	 */
	@Value("${CLUSTER_OPEN}")//官方建議給出默認值,如@Value("${CLUSTER_OPEN:ON}")--默認值爲ON
	private String clusteropen;
	/**
	 * 當前註冊中心name
	 */
	@Value("${REGISTER_CENTER_NAME}")
	private String curRegisterCenterName;
	/**
	 * zookkeeper相關的配置
	 */
	@Value("${ZK_ADDR}")
	private String zkAddr;
	@Value("${ZK_SESSION_OUTTIME}")
	private int zkSessionOutTime;
	//初次重試時間
	@Value("${ZK_BASE_SLEEP_TIME_MS}")
	private int zkBaseSleepTimeMs;
	//最大重試次數
	@Value("${ZK_MAX_RETRIES}")
	private int zkMaxRetries;
	/**
	 * 監聽namespace爲TEST2.RegistryCenter的變化
	 * @param changeEvent
	 */
	@ApolloConfigChangeListener(curNamespace)
	private void anotherOnChange(ConfigChangeEvent changeEvent) {
		if(curNamespace.equals(changeEvent.getNamespace())){
			Set<String> propertySet = changeEvent.changedKeys();
			for (String property : propertySet) {
				String newval = changeEvent.getChange(property).getNewValue();
//				System.out.println("old = " + clusteropen +";new = "+newval);
				/**
				 * 親測   變化後 properties對象通過Value註解賦值的屬性會自動變動到最新值,不過捕獲事件到屬性值
				 * 變化有100毫秒左右的延遲!
				 * 可以在app.properties中增加“apollo.autoUpdateInjectedSpringProperties=false”配置
				 * 關閉Value註解關聯的屬性自動刷新
				 */


				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					logger.error(e.getMessage());
				}finally {
					
					try {
						//do something
					} catch (InterruptedException e) {
						logger.error("註冊中心相關配置參數設定失敗"+e.getMessage());
					}
				}
				
			}
		}
	}
	
	public String getClusteropen() {
		return clusteropen;
	}
	public String getCurRegisterCenterName() {
		return curRegisterCenterName;
	}
	public String getZkAddr() {
		return zkAddr;
	}
	public int getZkSessionOutTime() {
		return zkSessionOutTime;
	}
	public int getZkBaseSleepTimeMs() {
		return zkBaseSleepTimeMs;
	}
	public int getZkMaxRetries() {
		return zkMaxRetries;
	}
	@Override
	public String toString() {
		return "RegistryCenterProperties [clusteropen=" + clusteropen + ", curRegisterCenterName="
				+ curRegisterCenterName + ", zkAddr=" + zkAddr + ", zkSessionOutTime=" + zkSessionOutTime
				+ ", zkBaseSleepTimeMs=" + zkBaseSleepTimeMs + ", zkMaxRetries=" + zkMaxRetries + "]";
	}
	
}

需要使用對應配置時,只需要將RegistryCenterProperties注入到對應類中而後通過RegistryCenterProperties的get方法,比便可獲取到相應的配置了

三、總結

1.apollo的使用方式有好幾種,我這裏只演示了使用spring方式引入apollo client的例子,不過大家也開出來了,其使用還是比較簡單的,而且如果你原來項目中本來就是使用@value()註解形式引入配置文件的話,那麼對已有代碼的改動是很小的!我已經將我一個項目配置切換到apollo了。

2.要想在實際項目中使用的話,必須要考慮配置文件的動態配置策略,尤其是形如:zookeeper、消息中間件、分佈式緩存等,一旦他的配置信息有變動,如何在不重啓應用的情況下,快速切換對應的client,這個問題是必須要解決的,這裏給大家提供一個思路(因爲我就是這樣子做的):譬如消息中間件,項目啓動的時候需要將通過javaAPI實例化的client保存起來,然後等到監控到其對應的配置信息發生變動時,直接關閉原client,關閉之後利用新的配置信息重新實例化一個newclient對象!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章