一澈蝙、 客戶端設(shè)計(jì)
上圖簡(jiǎn)要描述了Apollo客戶端的實(shí)現(xiàn)原理
1、客戶端和服務(wù)端保持了一個(gè)長(zhǎng)連接撵幽,從而能第一時(shí)間獲得配置更新的推送灯荧。(通過Http Long Polling實(shí)現(xiàn))
2、客戶端還會(huì)定時(shí)從Apollo配置中心服務(wù)端拉取應(yīng)用的最新配置盐杂。
(1)逗载、這是一個(gè)fallback機(jī)制,為了防止推送機(jī)制失效導(dǎo)致配置不更新
(2)链烈、客戶端定時(shí)拉取會(huì)上報(bào)本地版本厉斟,所以一般情況下,對(duì)于定時(shí)拉取的操作强衡,服務(wù)端都會(huì)返回304-Not Modified
(3)擦秽、定時(shí)頻率默認(rèn)為每5分鐘拉取一次,客戶端也可以通過在運(yùn)行是指定System Property:apollo.refreshInterval來覆蓋食侮,單位為分鐘
(4)号涯、客戶端會(huì)把從服務(wù)端獲取到的配置在本地文件緩存一份
遇到服務(wù)不可用,或網(wǎng)絡(luò)不通的時(shí)候锯七,依然能從本地恢復(fù)配置
(5)、應(yīng)用程序可以從Apollo客戶端獲取最新的配置誉己、訂閱配置更新通知
二眉尸、 準(zhǔn)備工作
1、環(huán)境要求
Java1.7+
Guava15.0+ : Apollo客戶端默認(rèn)會(huì)引用Guava 19巨双,如果你的項(xiàng)目引用了其它版本噪猾,請(qǐng)確保版本號(hào)大于等于15.0
注:對(duì)于Apollo客戶端,如果有需要的話筑累,可以做少量代碼修改來降級(jí)到Java 1.6,如需更改可以參照官方說明Issue 483
2袱蜡、必選設(shè)置
Apollo客戶端依賴于AppId, Apollo Meta Server等環(huán)境信息來工作,所以請(qǐng)確保閱讀下面的說明并且做正確的配置:
(1)慢宗、AppId
AppId是應(yīng)用的身份信息坪蚁,是從服務(wù)端獲取配置的一個(gè)重要信息奔穿。
有以下3種方式設(shè)置,按照優(yōu)先級(jí)從高到低分別為:
A: System Property
Apollo 0.7.0+支持通過System Property傳入app.id信息敏晤,如:
-Dapp.id=YOUR-APP-ID
B: Spring Boot application.properties
Apollo 1.0.0+支持通過Spring Boot的application.properties文件配置贱田,如
app.id=YOUR-APP-ID
C: app.properties
確保classpath:/META-INF/app.properties文件存在,并且其中內(nèi)容形如:
app.id=YOUR-APP-ID
注:app.id是用來標(biāo)識(shí)應(yīng)用身份的唯一id嘴脾,格式為string男摧。
(2)、Apollo Meta Server
Apollo支持應(yīng)用在不同的環(huán)境有不同的配置译打,所以需要在運(yùn)行提供給Apollo客戶端當(dāng)前環(huán)境的Apollo Meta Server信息耗拓。默認(rèn)情況下,meta server和config service是部署在同一個(gè)JVM進(jìn)程奏司,所以meta server的地址就是config service的地址帆离。
為了實(shí)現(xiàn)meta server的高可用,推薦通過SLB(Software Load Balancer)做動(dòng)態(tài)負(fù)載均衡结澄。Meta server地址也可以填入IP哥谷,如http://1.1.1.1:8080,http://2.2.2.2:8080,不過生產(chǎn)環(huán)境還是建議使用域名(走slb)麻献,因?yàn)闄C(jī)器擴(kuò)容们妥、縮容等都可能導(dǎo)致IP列表的變化。
1.0.0版本開始支持以下方式配置apollo meta server信息勉吻,按照優(yōu)先級(jí)從高到低分別為:
A:通過Java System Property apollo.meta
可以通過Java的System Property apollo.meta來指定
在Java程序啟動(dòng)腳本中监婶,可以指定-Dapollo.meta=http://config-service-url
如果是運(yùn)行jar文件,需要注意格式是java -Dapollo.meta=http://config-service-url -jar xxx.jar
也可以通過程序指定齿桃,如System.setProperty("apollo.meta", "http://config-service-url");
B:通過Spring Boot的配置文件
可以在Spring Boot的application.properties或bootstrap.properties中指定apollo.meta=http://config-service-url
C:通過操作系統(tǒng)的System EnvironmentAPOLLO_META
可以通過操作系統(tǒng)的System Environment APOLLO_META來指定
注意key為全大寫惑惶,且中間是_分隔
D:通過server.properties配置文件
可以在server.properties配置文件中指定apollo.meta=http://config-service-url
對(duì)于Mac/Linux,文件位置為/opt/settings/server.properties
對(duì)于Windows短纵,文件位置為C:\opt\settings\server.properties
E:通過app.properties配置文件
可以在classpath:/META-INF/app.properties指定apollo.meta=http://config-service-url
F:通過Java system property ${env}_meta
如果當(dāng)前env是dev带污,那么用戶可以配置-Ddev_meta=http://config-service-url
使用該配置方式,那么就必須要正確配置Environment
G:通過操作系統(tǒng)的System Environment ${ENV}_META (1.2.0版本開始支持)
如果當(dāng)前env是dev香到,那么用戶可以配置操作系統(tǒng)的System Environment DEV_META=http://config-service-url
注意key為全大寫
使用該配置方式鱼冀,那么就必須要正確配置Environment
H:通過apollo-env.properties文件
用戶也可以創(chuàng)建一個(gè)apollo-env.properties,放在程序的classpath下悠就,或者放在spring boot應(yīng)用的config目錄下
使用該配置方式千绪,那么就必須要正確配置Environment
文件內(nèi)容形如:
dev.meta=http://1.1.1.1:8080
fat.meta=http://apollo.fat.xxx.com
uat.meta=http://apollo.uat.xxx.com
pro.meta=http://apollo.xxx.com
如果通過以上各種手段都無法獲取到Meta Server地址,Apollo最終會(huì)fallback到http://apollo.meta作為Meta Server地址
3梗脾、本地緩存路徑
Apollo客戶端會(huì)把從服務(wù)端獲取到的配置在本地文件系統(tǒng)緩存一份荸型,用于在遇到服務(wù)不可用,或網(wǎng)絡(luò)不通的時(shí)候炸茧,依然能從本地恢復(fù)配置瑞妇,不影響應(yīng)用正常運(yùn)行稿静。
本地緩存路徑默認(rèn)位于以下路徑,所以請(qǐng)確保/opt/data或C:\opt\data\目錄存在踪宠,且應(yīng)用有讀寫權(quán)限自赔。
Mac/Linux: /opt/data/{appId}/config-cache
Windows: C:\opt\data\{appId}\config-cache
4、Environment
Environment可以通過以下3種方式的任意一個(gè)配置:
(1)柳琢、通過Java System Property
可以通過Java的System Property env來指定環(huán)境
在Java程序啟動(dòng)腳本中绍妨,可以指定-Denv=YOUR-ENVIRONMENT
如果是運(yùn)行jar文件,需要注意格式是java -Denv=YOUR-ENVIRONMENT -jar xxx.jar
注意key為全小寫
(2)柬脸、通過操作系統(tǒng)的System Environment
還可以通過操作系統(tǒng)的System Environment ENV來指定
注意key為全大寫
通過配置文件
(3)他去、最后一個(gè)推薦的方式是通過配置文件來指定env=YOUR-ENVIRONMENT
對(duì)于Mac/Linux,文件位置為/opt/settings/server.properties
對(duì)于Windows倒堕,文件位置為C:\opt\settings\server.properties
文件內(nèi)容形如:
env=DEV
目前灾测,env支持以下幾個(gè)值(大小寫不敏感):
DEV:Development environment
FAT:Feature Acceptance Test environment
UAT:User Acceptance Test environment
PRO:Production environment
三、 Maven依賴
在項(xiàng)目中引入maven依賴
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.1.0</version>
</dependency>
四垦巴、 客戶端用法
Apollo支持API方式和Spring整合方式媳搪,該怎么選擇用哪一種方式?
API方式靈活骤宣,功能完備秦爆,配置值實(shí)時(shí)更新(熱發(fā)布),支持所有Java環(huán)境憔披。
Spring方式接入簡(jiǎn)單等限,結(jié)合Spring有N種酷炫的玩法,如
前提是需要將Config交由Spring管理
Placeholder方式:
代碼中使用芬膝,如:@Value("{someKeyFromApollo:someDefaultValue}
Spring boot的@ConfigurationProperties方式
從v0.10.0開始的版本支持placeholder在運(yùn)行時(shí)自動(dòng)更新疹吃,具體參見PR #972挠铲。(v0.10.0之前的版本在配置變化后不會(huì)重新注入甜橱,需要重啟才會(huì)更新,如果需要配置值實(shí)時(shí)更新锈遥,可以參考后續(xù)3.2.2 Spring Placeholder的使用的說明)
Spring方式也可以結(jié)合API方式使用纫事,如注入Apollo的Config對(duì)象,就可以照常通過API方式獲取配置了:
@ApolloConfig
private Config config; //inject config for namespace application
1所灸、API使用方式
API方式是最簡(jiǎn)單、高效使用Apollo配置的方式炫七,不依賴Spring框架即可使用爬立。
(1)、獲取默認(rèn)namespace的配置(application)
Config config = ConfigService.getAppConfig(); //config instance is singleton for each namespace and is never null
String someKey = "someKeyFromDefaultNamespace";
String someDefaultValue = "someDefaultValueForTheKey";
String value = config.getProperty(someKey, someDefaultValue);
通過上述的config.getProperty可以獲取到someKey對(duì)應(yīng)的實(shí)時(shí)最新的配置值万哪。
另外侠驯,配置值從內(nèi)存中獲取抡秆,所以不需要應(yīng)用自己做緩存。
(2)吟策、獲取公共namespace的配置
String somePublicNamespace = "CAT";
Config config = ConfigService.getConfig(somePublicNamespace); //config instance is singleton for each namespace and is never null
String someKey = "someKeyFromPublicNamespace";
String someDefaultValue = "someDefaultValueForTheKey";
String value = config.getProperty(someKey, someDefaultValue);
(3)儒士、獲取非properties格式namespace的配置
String someNamespace = "test";
ConfigFile configFile = ConfigService.getConfigFile(someNamespace, ConfigFileFormat.YML);
String content = configFile.getContent();
System.out.println(content);
2、基于java配置
注意@EnableApolloConfig要和@Configuration一起使用檩坚,不然不會(huì)生效着撩。
(1)、注入默認(rèn)namespace的配置到Spring中
//這個(gè)是最簡(jiǎn)單的配置形式匾委,一般應(yīng)用用這種形式就可以了拖叙,用來指示Apollo注入application namespace的配置到Spring環(huán)境中
@Configuration
@EnableApolloConfig
public class AppConfig {
}
(2)、注入多個(gè)namespace的配置到spring中
@Configuration
@EnableApolloConfig // 注意@EnableApolloConfig要和@Configuration一起使用赂乐,不然不會(huì)生效薯鳍。
public class ApolloConfig {
}
@Configuration
@EnableApolloConfig(value = {"TEST1.sys_application","sys_application"})
public class AnotherAppConfig {
}
(3)、注入多個(gè)namespace挨措,并且指定順序
@Configuration
@EnableApolloConfig(order = 1) // 注意@EnableApolloConfig要和@Configuration一起使用挖滤,不然不會(huì)生效。
public class ApolloConfig {
}
@Configuration
@EnableApolloConfig(value = {"TEST1.sys_application","sys_application"}, order = 2)
public class AnotherAppConfig {
}
3浅役、Spring Boot集成方式
Spring Boot除了支持上述兩種集成方式以外斩松,還支持通過application.properties/bootstrap.properties來配置,該方式能使配置在更早的階段注入担租,比如使用@ConditionalOnProperty的場(chǎng)景或者是有一些spring-boot-starter在啟動(dòng)階段就需要讀取配置做一些事情(如dubbo-spring-boot-project)砸民,所以對(duì)于Spring Boot環(huán)境建議通過以下方式來接入Apollo(需要0.10.0及以上版本)。
使用方式很簡(jiǎn)單奋救,只需要在application.properties/bootstrap.properties中按照如下樣例配置即可岭参。
(1)、注入默認(rèn)application namespace的配置示例
# will inject 'application' namespace in bootstrap phase
apollo.bootstrap.enabled = true
(2)尝艘、注入非默認(rèn)application namespace或多個(gè)namespace的配置示例
apollo.bootstrap.enabled = true
# will inject 'application' and 'FX.apollo' namespaces in bootstrap phase
apollo.bootstrap.namespaces = application,FX.apollo
注:紅色部分的書寫順序決定了優(yōu)先級(jí)
4演侯、Spring Annotation支持
Apollo同時(shí)還增加了幾個(gè)新的Annotation來簡(jiǎn)化在Spring環(huán)境中的使用。
@ApolloConfig
用來自動(dòng)注入Config對(duì)象
@ApolloConfigChangeListener
用來自動(dòng)注冊(cè)ConfigChangeListener
@ApolloJsonValue
用來把配置的json字符串自動(dòng)注入為對(duì)象
使用樣例如下
public class TestApolloAnnotationBean {
@ApolloConfig
private Config config; //inject config for namespace application
@ApolloConfig("application")
private Config anotherConfig; //inject config for namespace application
@ApolloConfig("FX.apollo")
private Config yetAnotherConfig; //inject config for namespace FX.apollo
/**
* ApolloJsonValue annotated on fields example, the default value is specified as empty list - []
* <br />
* jsonBeanProperty=[{"someString":"hello","someInt":100},{"someString":"world!","someInt":200}]
*/
@ApolloJsonValue("${jsonBeanProperty:[]}")
private List<JsonBean> anotherJsonBeans;
@Value("${batch:100}")
private int batch;
//config change listener for namespace application
@ApolloConfigChangeListener
private void someOnChange(ConfigChangeEvent changeEvent) {
//update injected value of batch if it is changed in Apollo
if (changeEvent.isChanged("batch")) {
batch = config.getIntProperty("batch", 100);
}
}
//config change listener for namespace application
@ApolloConfigChangeListener("application")
private void anotherOnChange(ConfigChangeEvent changeEvent) {
//do something
}
//config change listener for namespaces application and FX.apollo
@ApolloConfigChangeListener({"application", "FX.apollo"})
private void yetAnotherOnChange(ConfigChangeEvent changeEvent)
//do something
}
//example of getting config from Apollo directly
//this will always return the latest value of timeout
public int getTimeout() {
return config.getIntProperty("timeout", 200);
}
//example of getting config from injected value
//the program needs to update the injected value when batch is changed in Apollo using @ApolloConfigChangeListener shown above
public int getBatch() {
return this.batch;
}
private static class JsonBean{
private String someString;
private int someInt;
}
}
在Configuration類中按照下面的方式使用:
@Configuration
@EnableApolloConfig
public class AppConfig {
@Bean
public TestApolloAnnotationBean testApolloAnnotationBean() {
return new TestApolloAnnotationBean();
}
}