一、背景
通過上一篇日誌,我們已經將一個單機版的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對象!