攜程Apollo統一配置中心的搭建和使用(java)
一.Apollo配置中心介紹
1、What is Apollo
1.1 Apollo簡介
Apollo(阿波羅)是攜程框架部門研發的開源配置管理中心,能夠集中化管理應用不同環境、不同叢集的配置,配置修改後能夠實時推送到應用端,並且具備規範的許可權、流程治理等特性。
Apollo支援4個維度管理Key-Value格式的配置:
- application (應用)
- environment (環境)
- cluster (叢集)
- namespace (名稱空間)
二、分散式部署指南
1.環境
1.1 Java
- Apollo服務端:1.8+
- Apollo客戶端:1.7+
可以通過如下命令檢查:
java -version
樣例輸出:
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)
1.2 MySQL
- 版本要求:5.6.5+
連線上MySQL後,可以通過如下命令檢查:
SHOW VARIABLES WHERE Variable_name = 'version';
Variable_name | Value |
---|---|
version | 5.7.11 |
1.3、環境
分散式部署需要事先確定部署的環境以及部署方式。
Apollo目前支援以下環境:
- DEV
- 開發環境
- FAT
- 測試環境,相當於alpha環境(功能測試)
- UAT
- 整合環境,相當於beta環境(迴歸測試)
2、部署步驟
部署步驟共三步:
- 建立資料庫
ApolloconfigDB(),
- Apollo服務端依賴於MySQL資料庫,所以需要事先建立並完成初始化
- 獲取安裝包:GitHub地址:https://github.com/ctripcorp/apollo/releases
- Apollo服務端安裝包共有3個:apollo-configservice, apollo-adminservice, apollo-portal
- 可以直接下載我們事先打好的安裝包,也可以自己通過原始碼構建
- Apollo客戶端jar包中由於會包含meta server資訊,無法上傳一個統一的jar包到中央倉庫
-
- 可以直接下載我們事先打好的安裝包,修改相應配置後上傳到自己公司的Maven私服
- 也可以直接通過原始碼構建並上傳到公司的Maven私服
- Apollo服務端安裝包共有3個:apollo-configservice, apollo-adminservice, apollo-portal
- 部署Apollo服務端
- 獲取安裝包後就可以部署到公司的測試和生產環境了
2.1 建立資料庫
Apollo服務端共需要兩個資料庫:ApolloPortalDB
和ApolloConfigDB
2.1.1 建立ApolloPortalDB
匯入成功後,可以通過執行以下sql語句來驗證:
select `Id`, `Key`, `Value`, `Comment` from `ApolloPortalDB`.`ServerConfig` limit 1;
Id | Key | Value | Comment |
---|---|---|---|
1 | apollo.portal.envs | dev | 可支援的環境列表 |
注:ApolloPortalDB只需要在生產環境部署一個即可
2.1.2 建立ApolloConfigDB
匯入成功後,可以通過執行以下sql語句來驗證:
select `Id`, `Key`, `Value`, `Comment` from `ApolloConfigDB`.`ServerConfig` limit 1;
Id | Key | Value | Comment |
---|
注:ApolloConfigDB需要在每個環境部署一套,如fat、uat和pro分別部署3套ApolloConfigDB
2.1.3 調整服務端配置
Apollo自身的一些配置是放在資料庫裡面的。
2.1.3.1 調整ApolloPortalDB配置
配置項統一儲存在ApolloPortalDB.ServerConfig表中。
1.apollo.portal.envs - 可支援的環境列表
預設值是dev,如果portal需要管理多個環境的話,以逗號分隔即可(大小寫不敏感),如:
DEV,FAT,UAT,PRO
注1:一套Portal可以管理多個環境,但是每個環境都需要獨立部署一套Config Service、Admin Service和ApolloConfigDB
注2:只在資料庫新增環境是不起作用的,還需要為apollo-portal和apollo-client新增新增環境對應的meta server地址。
2.organizations - 部門列表
Portal中新建的App都需要選擇部門,所以需要在這裡配置可選的部門資訊,樣例如下:
[{"orgId":"TEST1","orgName":"樣例部門1"},{"orgId":"TEST2","orgName":"樣例部門2"}]
3.superAdmin - Portal超級管理員
超級管理員擁有所有許可權
預設值apollo(預設使用者),多個賬號以英文逗號分隔(,)。
4.consumer.token.salt - consumer token salt
如果會使用開放平臺API的話,可以設定一個token salt。如果不使用,可以忽略。
5.wiki.address
portal上“幫助”連結的地址,預設是Apollo github的wiki首頁,可自行設定。
6.admin.createPrivateNamespace.switch
是否允許專案管理員建立private namespace。設定為true
允許建立,設定為false
則專案管理員在頁面上看不到建立private namespace的選項。
2.1.3.2 調整ApolloConfigDB配置
配置項統一儲存在ApolloConfigDB.ServerConfig表中,需要注意每個環境的ApolloConfigDB.ServerConfig都需要單獨配置。
1.eureka.service.url - Eureka服務Url
apollo-configservice和apollo-adminservice都需要向eureka服務註冊,需要配置eureka服務地址。預設apollo-configservice本身就是一個eureka服務,所以只需要填入apollo-configservice的地址即可,如有多個,用逗號分隔(注意不要忘了/eureka/字尾)。
如:內網開發環境
- 在DEV環境的ApolloConfigDB.ServerConfig表中設定eureka.service.url為:注1:這裡需要填寫本環境中全部的eureka服務地址,因為eureka需要互相複製註冊資訊
注2:如果希望將Config Service和Admin Service註冊到公司統一的Eureka上
pollo預設自帶了Eureka作為內部的註冊中心實現,一般情況下不需要考慮為Apollo單獨部署註冊中心。
如需要註冊到自己的Eureka,需修改Config Service:
@EnableEurekaClient @EnableAspectJAutoProxy @EnableAutoConfiguration // (exclude = EurekaClientConfigBean.class) @Configuration @EnableTransactionManagement @PropertySource(value = {"classpath:configservice.properties"}) @ComponentScan(basePackageClasses = {ApolloCommonConfig.class, ApolloBizConfig.class, ConfigServiceApplication.class, ApolloMetaServiceConfig.class}) public class ConfigServiceApplication { ... }
- 修改ApolloConfigDB.ServerConfig表中的
eureka.service.url
,指向自己的Eureka地址需要注意的是更改Eureka地址只需要改ApolloConfigDB.ServerConfig表中的
eureka.service.url
即可,不需要修改meta server地址。
2.namespace.lock.switch - 一次釋出只能有一個人修改開關,用於釋出稽核
這是一個功能開關,如果配置為true的話,那麼一次配置釋出只能是一個人修改,另一個釋出。
3.config-service.cache.enabled - 是否開啟配置快取
這是一個功能開關,如果配置為true的話,config service會快取載入過的配置資訊,從而加快後續配置獲取效能。
預設為false,開啟前請先評估總配置大小並調整config service記憶體配置。
2.2 獲取安裝包
可以通過兩種方式獲取安裝包:
- 直接下載安裝包
- 如果對Apollo的程式碼沒有定製需求,建議使用這種方式,可以省去本地打包的過程
- 測試V0.11.0版本,在下載安裝包,成功部署後,後臺預設賬號密碼會出現密碼錯誤,後來使用了原始碼構建
2.2.1 直接下載安裝包
下載安裝包,這裡就不說了,參考:apollo官網配置檔案
2.2.2 通過原始碼構建
下載原始碼V0.11.0,開啟工程:
2.2.2.1 配置資料庫連線資訊
編輯scripts/build.sh,修改ApolloPortalDB和ApolloConfigDB相關的資料庫連線串資訊。
注1:ApolloConfigDB在每個環境都需部署,不同的環境config-service和admin-service需要使用不同的資料庫引數打不同的包,portal和client只需要打一次包即可
注2:每個環境都需要獨立部署一套config-service、admin-service和ApolloConfigDB
- 也可在執行時指定:-Dserver.port=8100 -Dapollo_profile=github
- -Dspring.datasource.url=jdbc:mysql://192.168.0.*:3306/ApolloConfigDB?characterEncoding=utf8
- -Dspring.datasource.username=root -Dspring.datasource.password=123456
2.2.2.2 配置各環境meta service地址(configservice部署的地址)
Portal和Apollo客戶端,client需要在不同的環境訪問不同的meta service(apollo-configservice)地址,
後面版本也是可以再執行時指定: -Dapollo.meta=http://192.168.*.*:6080
2.2.2.3 執行編譯、打包
做完上述配置後,就可以執行編譯和打包了。
./build.sh
該指令碼會依次打包apollo-configservice, apollo-adminservice, apollo-portal和apollo-client。
注:由於ApolloConfigDB在每個環境都有部署,所以對不同環境的config-service和admin-service需要使用不同的資料庫連線資訊打不同的包,portal和client只需要打一次包即可
2.2.2.4 獲取apollo-configservice安裝包
位於apollo-configservice/target/
目錄下的apollo-configservice-x.x.x-github.zip
需要注意的是由於ApolloConfigDB在每個環境都有部署,所以對不同環境的config-service需要使用不同的資料庫引數打不同的包後分別部署
2.2.2.5 獲取apollo-adminservice安裝包
位於apollo-adminservice/target/
目錄下的apollo-adminservice-x.x.x-github.zip
需要注意的是由於ApolloConfigDB在每個環境都有部署,所以對不同環境的admin-service需要使用不同的資料庫引數打不同的包後分別部署
2.2.2.6 獲取apollo-portal安裝包
位於apollo-portal/target/
目錄下的apollo-portal-x.x.x-github.zip
2.2.2.7 獲取apollo-client相關jar包
由於客戶端jar包中會包含meta server資訊,無法上傳一個統一的jar包到中央倉庫,所以需要自己上傳到自己公司的Maven私服。
注:meta server資訊在打包後會寫入
apollo-core.jar
包中的apollo-env.properties檔案。
build.sh
中預設執行的命令是mvn clean install,所以會把apollo-client相關的jar包儲存到本地的maven倉庫(本地)。
如果有maven倉庫的deploy許可權,建議通過maven命令直接上傳,把install
修改為deploy
,同時按照下面的說明做對應配置即可。
注:deploy操作需要在.m2/settings.xml中設定
releases.repo
和snapshots.repo
屬性以及對應倉庫的使用者名稱和密碼(注意server的id必須是releases和snapshots),如:
<servers>
<server>
<id>releases</id>
<username>someUserName</username>
<password>somePassword</password>
</server>
<server>
<id>snapshots</id>
<username>someUserName</username>
<password>somePassword</password>
</server>
</servers>
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<releases.repo>http://${your_nexus_url}/nexus/content/repositories/${release-repository}</releases.repo>
<snapshots.repo>http://${your_nexus_url}/nexus/content/repositories/${snapshotrepository}</snapshots.repo>
</properties>
</profile>
</profiles>
或者也可以手工上傳本地倉庫中的apollo-client、apollo-core、apollo-buildtools的jar包、pom檔案和apollo的pom檔案,如下圖所示,需要把本地倉庫中的apollo相關檔案都上傳。
2.3 部署Apollo服務端
2.3.1 部署apollo-configservice
將對應環境的apollo-configservice-x.x.x-github.zip
上傳到伺服器上,
解壓後執行scripts/startup.sh即可。如需停止服務,執行scripts/shutdown.sh.(預設是8080埠,端口占用需修改埠號,vim開啟startup.sh,修改SERVER_PORT,修改了埠號,如用的預設eureka,需修改對應資料庫配置,參考2.1.3.2)
記得在startup.sh中按照實際的環境設定一個JVM記憶體,以下是我們的預設設定,供參考:
export JAVA_OPTS="-server -Xms6144m -Xmx6144m -Xss256k -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=384m -XX:NewSize=4096m -XX:MaxNewSize=4096m -XX:SurvivorRatio=18"
注1:如果需要修改JVM引數,可以修改startup.sh的
JAVA_OPTS
部分。
注2:如要調整服務的日誌輸出路徑,可以修改startup.sh中的
LOG_DIR
。
注3:如要調整服務的監聽埠,可以修改startup.sh中的
SERVER_PORT
。另外apollo-configservice同時承擔meta server職責,如果要修改埠,注意要同時修改scripts/build.sh中的meta server url資訊以及ApolloConfigDB.ServerConfig表中的eureka.service.url
配置項。也可以再執行時指定需要註冊到的Eureka:-
Deureka.instance.ip-address=http://${指定的IP}
,-Deureka.instance.homePageUrl=http://${指定的IP}:${指定的Port}
注4:如果ApolloConfigDB.ServerConfig的eureka.service.url只配了當前正在啟動的機器的話,在啟動apollo-configservice的過程中會在日誌中輸出eureka註冊失敗的資訊,如
com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused
。需要注意的是,這個是預期的情況,因為apollo-configservice需要向Meta Server(它自己)註冊服務,但是因為在啟動過程中,自己還沒起來,所以會報這個錯。後面會進行重試的動作,所以等自己服務起來後就會註冊正常了。
2.3.2 部署apollo-adminservice
將對應環境的apollo-adminservice-x.x.x-github.zip
上傳到伺服器上,解壓後執行scripts/startup.sh即可。如需停止服務,執行scripts/shutdown.sh.(預設是8090埠,修改和config一樣)
記得在startup.sh中按照實際的環境設定一個JVM記憶體,以下是我們的預設設定,供參考:
export JAVA_OPTS="-server -Xms2560m -Xmx2560m -Xss256k -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=384m -XX:NewSize=1024m -XX:MaxNewSize=1024m -XX:SurvivorRatio=22"
注1:如果需要修改JVM引數,可以修改startup.sh的
JAVA_OPTS
部分。
注2:如要調整服務的日誌輸出路徑,可以修改startup.sh中的
LOG_DIR
。
注3:如要調整服務的監聽埠,可以修改startup.sh中的
SERVER_PORT
。
2.3.3 部署apollo-portal
將apollo-portal-x.x.x-github.zip
上傳到伺服器上,解壓後執行scripts/startup.sh即可。如需停止服務,執行scripts/shutdown.sh.(預設是8070埠,修改和config一樣)
記得在startup.sh中按照實際的環境設定一個JVM記憶體,以下是我們的預設設定,供參考:
export JAVA_OPTS="-server -Xms4096m -Xmx4096m -Xss256k -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=384m -XX:NewSize=1536m -XX:MaxNewSize=1536m -XX:SurvivorRatio=22"
注1:如果需要修改JVM引數,可以修改startup.sh的
JAVA_OPTS
部分。
注2:如要調整服務的日誌輸出路徑,可以修改startup.sh中的
LOG_DIR
。
注3:如要調整服務的監聽埠,可以修改startup.sh中的
SERVER_PORT
。
三、Java客戶端使用指南
1、準備工作
1.1 環境要求
- Java: 1.7+
- Guava: 15.0+
- Apollo客戶端預設會引用Guava 19,如果你的專案引用了其它版本,請確保版本號大於等於15.0
1.2 必選設定
Apollo客戶端依賴於AppId
,Environment
等環境資訊來工作,所以請確保閱讀下面的說明並且做正確的配置:
1.2.1 AppId
AppId是應用的身份資訊,是從服務端獲取配置的一個重要資訊。
請確保classpath:/META-INF/app.properties檔案存在,並且其中內容形如:
app.id=YOUR-APP-ID
檔案位置參考如下:
v0.7.0版本後,Apollo也支援通過System Property傳入app.id資訊,如
-Dapp.id=YOUR-APP-ID
注:app.id是用來標識應用身份的唯一id,格式為string。
1.2.2 Environment
Apollo支援應用在不同的環境有不同的配置,所以Environment是另一個從伺服器獲取配置的重要資訊。
Environment可以通過以下3種方式的任意一個配置:
-
通過Java System Property
- 可以通過Java的System Property
env
來指定環境 - 在Java程式啟動指令碼中,可以指定
-Denv=YOUR-ENVIRONMENT
- 如果是執行jar檔案,需要注意格式是
java -Denv=YOUR-ENVIRONMENT -jar xxx.jar
- 如果是執行jar檔案,需要注意格式是
- 注意key為全小寫
- 可以通過Java的System Property
-
通過作業系統的System Environment
- 還可以通過作業系統的System Environment
ENV
來指定 - 注意key為全大寫
- 還可以通過作業系統的System Environment
-
通過配置檔案
- 最後一個推薦的方式是通過配置檔案來指定
env=YOUR-ENVIRONMENT
- 對於Mac/Linux,檔案位置為
/opt/settings/server.properties
- 對於Windows,檔案位置為
C:\opt\settings\server.properties
- 最後一個推薦的方式是通過配置檔案來指定
檔案內容形如:
env=DEV
目前,env
支援以下幾個值(大小寫不敏感):
- DEV
- Development environment
- FAT
- Feature Acceptance Test environment
- UAT
- User Acceptance Test environment
- PRO
- Production environment
1.2.3 本地快取路徑
Apollo客戶端會把從服務端獲取到的配置在本地檔案系統快取一份,用於在遇到服務不可用,或網路不通的時候,依然能從本地恢復配置,不影響應用正常執行。
本地快取路徑位於以下路徑,所以請確保/opt/data
或C:\opt\data\
目錄存在,且應用有讀寫許可權。
- Mac/Linux: /opt/data/{appId}/config-cache
- Windows: C:\opt\data\{appId}\config-cache
本地配置檔案會以下面的檔名格式放置於本地快取路徑下:
{appId}+{cluster}+{namespace}.properties
- appId就是應用自己的appId,如100004458
- cluster就是應用使用的叢集,一般在本地模式下沒有做過配置的話,就是default
- namespace就是應用使用的配置namespace,一般是application
檔案內容以properties格式儲存,比如如果有兩個key,一個是request.timeout,另一個是batch,那麼檔案內容就是如下格式:
request.timeout=2000
batch=2000
注:本地快取路徑也可用於容災目錄,如果應用在所有config service都掛掉的情況下需要擴容,那麼也可以先把配置從已有機器上的快取路徑複製到新機器上的相同快取路徑
1.2.4 可選設定
Cluster(叢集)
Apollo支援配置按照叢集劃分,也就是說對於一個appId和一個環境,對不同的叢集可以有不同的配置。
如果需要使用這個功能,你可以通過以下方式來指定執行時的叢集:
-
通過Java System Property
- 我們可以通過Java的System Property設定
apollo.cluster
來指定執行時叢集(注意key為全小寫) - 例如,可以在程式啟動時通過
-Dapollo.cluster=SomeCluster
來指定執行時的叢集為SomeCluster
- 我們可以通過Java的System Property設定
-
通過配置檔案
- 首先確保
/opt/settings/server.properties
(Mac/Linux)或C:\opt\settings\server.properties
(Windows)在目標機器上存在 - 在這個檔案中,可以設定資料中心叢集,如
idc=xxx
- 注意key為全小寫
- 首先確保
Cluster Precedence(叢集順序)
-
如果
apollo.cluster
和idc
同時指定:- 我們會首先嚐試從
apollo.cluster
指定的叢集載入配置 - 如果沒找到任何配置,會嘗試從
idc
指定的叢集載入配置 - 如果還是沒找到,會從預設的叢集(
default
)載入
- 我們會首先嚐試從
-
如果只指定了
apollo.cluster
:- 我們會首先嚐試從
apollo.cluster
指定的叢集載入配置 - 如果沒找到,會從預設的叢集(
default
)載入
- 我們會首先嚐試從
-
如果只指定了
idc
:- 我們會首先嚐試從
idc
指定的叢集載入配置 - 如果沒找到,會從預設的叢集(
default
)載入
- 我們會首先嚐試從
-
如果
apollo.cluster
和idc
都沒有指定:- 我們會從預設的叢集(
default
)載入配置
- 我們會從預設的叢集(
2、Maven Dependency
由於客戶端jar包中會包含meta server資訊,無法上傳一個統一的jar包到中央倉庫,所以請按照分散式部署指南的文件說明打包並上傳到自己公司的Maven私服。應用在實際使用時只需要按照如下方式引入即可。
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>0.10.2</version>
</dependency>
3、客戶端用法
本文使用的是springboot;其它方法請參考官方文件
3.1 API使用方式
3.2 Spring整合方式
3.2.1 配置
如果是Spring Boot環境,建議參照3.2.1.3配置(這裡是使用springboot的)。
Apollo也支援和Spring整合(Spring 3.1.1+),只需要做一些簡單的配置就可以了。
Apollo目前既支援比較傳統的基於XML
的配置,也支援目前比較流行的基於Java(推薦)
的配置。
需要注意的是,如果之前有使用org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
的,請替換成org.springframework.context.support.PropertySourcesPlaceholderConfigurer
。Spring 3.1以後就不建議使用PropertyPlaceholderConfigurer了,要改用PropertySourcesPlaceholderConfigurer。
如果之前有使用<context:property-placeholder>
,請注意xml中引入的spring-context.xsd
版本需要是3.1以上(一般只要沒有指定版本會自動升級的),建議使用不帶版本號的形式引入,如:http://www.springframework.org/schema/context/spring-context.xsd
3.2.1.1 基於XML的配置
注:需要把apollo相關的xml namespace加到配置檔案頭上,不然會報xml語法錯誤。
1.注入預設namespace的配置到Spring中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:apollo="http://www.ctrip.com/schema/apollo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.ctrip.com/schema/apollo http://www.ctrip.com/schema/apollo.xsd">
<!-- 這個是最簡單的配置形式,一般應用用這種形式就可以了,用來指示Apollo注入application namespace的配置到Spring環境中 -->
<apollo:config/>
<bean class="com.ctrip.framework.apollo.spring.TestXmlBean">
<property name="timeout" value="${timeout:100}"/>
<property name="batch" value="${batch:200}"/>
</bean>
</beans>
2.注入多個namespace的配置到Spring中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:apollo="http://www.ctrip.com/schema/apollo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.ctrip.com/schema/apollo http://www.ctrip.com/schema/apollo.xsd">
<!-- 這個是最簡單的配置形式,一般應用用這種形式就可以了,用來指示Apollo注入application namespace的配置到Spring環境中 -->
<apollo:config/>
<!-- 這個是稍微複雜一些的配置形式,指示Apollo注入FX.apollo和FX.soa namespace的配置到Spring環境中 -->
<apollo:config namespaces="FX.apollo,FX.soa"/>
<bean class="com.ctrip.framework.apollo.spring.TestXmlBean">
<property name="timeout" value="${timeout:100}"/>
<property name="batch" value="${batch:200}"/>
</bean>
</beans>
3.注入多個namespace,並且指定順序
Spring的配置是有順序的,如果多個property source都有同一個key,那麼最終是順序在前的配置生效。
apollo:config如果不指定order,那麼預設是最低優先順序。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:apollo="http://www.ctrip.com/schema/apollo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.ctrip.com/schema/apollo http://www.ctrip.com/schema/apollo.xsd">
<apollo:config order="2"/>
<!-- 這個是最複雜的配置形式,指示Apollo注入FX.apollo和FX.soa namespace的配置到Spring環境中,並且順序在application前面 -->
<apollo:config namespaces="FX.apollo,FX.soa" order="1"/>
<bean class="com.ctrip.framework.apollo.spring.TestXmlBean">
<property name="timeout" value="${timeout:100}"/>
<property name="batch" value="${batch:200}"/>
</bean>
</beans>
3.2.1.2 基於Java的配置(推薦)
相對於基於XML的配置,基於Java的配置是目前比較流行的方式。
注意@EnableApolloConfig
要和@Configuration
一起使用,不然不會生效。
1.注入預設namespace的配置到Spring中
//這個是最簡單的配置形式,一般應用用這種形式就可以了,用來指示Apollo注入application namespace的配置到Spring環境中
@Configuration
@EnableApolloConfig
public class AppConfig {
@Bean
public TestJavaConfigBean javaConfigBean() {
return new TestJavaConfigBean();
}
}
2.注入多個namespace的配置到Spring中
@Configuration
@EnableApolloConfig
public class SomeAppConfig {
@Bean
public TestJavaConfigBean javaConfigBean() {
return new TestJavaConfigBean();
}
}
//這個是稍微複雜一些的配置形式,指示Apollo注入FX.apollo和FX.soa namespace的配置到Spring環境中
@Configuration
@EnableApolloConfig({"FX.apollo", "FX.soa"})
public class AnotherAppConfig {}
3.注入多個namespace,並且指定順序
//這個是最複雜的配置形式,指示Apollo注入FX.apollo和FX.soa namespace的配置到Spring環境中,並且順序在application前面
@Configuration
@EnableApolloConfig(order = 2)
public class SomeAppConfig {
@Bean
public TestJavaConfigBean javaConfigBean() {
return new TestJavaConfigBean();
}
}
@Configuration
@EnableApolloConfig(value = {"FX.apollo", "FX.soa"}, order = 1)
public class AnotherAppConfig {}
3.2.1.3 在Spring Boot初始bootstrap階段注入配置
Apollo會在Spring的postProcessBeanFactory階段注入配置到Spring的Environment中,早於bean的初始化階段,所以對於普通的bean注入配置場景已經能很好的滿足。
不過Spring Boot有一些場景需要配置在更早的階段注入,比如使用@ConditionalOnProperty
的場景或者是有一些spring-boot-starter在啟動階段就需要讀取配置做一些事情(如spring-boot-starter-dubbo),所以對於Spring Boot環境建議通過以下方式來接入Apollo(需要0.10.0及以上版本)。
使用方式很簡單,只需要在application.properties/bootstrap.properties中按照如下樣例配置即可。
- 在bootstrap階段注入預設
application
namespace的配置示例
# will inject 'application' namespace in bootstrap phase
apollo.bootstrap.enabled = true
- 在bootstrap階段注入非預設
application
namespace或多個namespace的配置示例
apollo.bootstrap.enabled = true
# will inject 'application' and 'FX.apollo' namespaces in bootstrap phase
apollo.bootstrap.namespaces = application,FX.apollo
3.2.2 Spring Placeholder的使用
Spring應用通常會使用Placeholder來注入配置,使用的格式形如${someKey:someDefaultValue},如${timeout:100}。冒號前面的是key,冒號後面的是預設值。
建議在實際使用時儘量給出預設值,以免由於key沒有定義導致執行時錯誤。
從v0.10.0開始的版本支援placeholder在執行時自動更新,具體參見PR #972。
如果需要關閉placeholder在執行時自動更新功能,可以通過以下兩種方式關閉:
-
通過設定System Property
apollo.autoUpdateInjectedSpringProperties
,如啟動時傳入-Dapollo.autoUpdateInjectedSpringProperties=false
-
通過設定META-INF/app.properties中的
apollo.autoUpdateInjectedSpringProperties
屬性,如
app.id=SampleApp
apollo.autoUpdateInjectedSpringProperties=false
對於v0.10.0之前的版本,placeholder只會在啟動的時候賦值,在執行過程中即便在Apollo上修改釋出了配置,placeholder的值也不會更新。如果需要執行時動態更新placeholder的值,有以下幾種方式:
- 程式監聽Apollo的ConfigChangeListener(如@ApolloConfigChangeListener),然後通過自己的程式碼來更新placeholder的值。
- 不使用placeholder方式獲取配置,而是直接從config物件中獲取配置。樣例程式碼可以參考3.2.3 Spring Annotation支援的
getBatch
和getTimeout
方法。- 【推薦】使用Spring Cloud的RefreshScope
- 在需要重新整理配置的bean上加@RefreshScope
- 加一個Apollo的ConfigChangeListener,然後在配置變化時呼叫RefreshScope的refreshAll()或者refresh(String name)的方法。
3.2.2.2 Java Config使用方式
假設我有一個TestJavaConfigBean,通過Java Config的方式還可以使用@Value的方式注入:
public class TestJavaConfigBean {
@Value("${timeout:100}")
private int timeout;
private int batch;
@Value("${batch:200}")
public void setBatch(int batch) {
this.batch = batch;
}
public int getTimeout() {
return timeout;
}
public int getBatch() {
return batch;
}
}
在Configuration類中按照下面的方式使用(假設應用預設的application namespace中有timeout
和batch
的配置項):
@Configuration
@EnableApolloConfig
public class AppConfig {
@Bean
public TestJavaConfigBean javaConfigBean() {
return new TestJavaConfigBean();
}
}
3.2.2.3 ConfigurationProperties使用方式
Apollo也支援這種方式,下面的例子會把redis.cache.expireSeconds
和redis.cache.commandTimeout
分別注入到SampleRedisConfig的expireSeconds
和commandTimeout
欄位中。
@ConfigurationProperties(prefix = "redis.cache")
public class SampleRedisConfig {
private int expireSeconds;
private int commandTimeout;
public void setExpireSeconds(int expireSeconds) {
this.expireSeconds = expireSeconds;
}
public void setCommandTimeout(int commandTimeout) {
this.commandTimeout = commandTimeout;
}
}
在Configuration類中按照下面的方式使用(假設應用預設的application namespace中有redis.cache.expireSeconds
和redis.cache.commandTimeout
的配置項):
@Configuration
@EnableApolloConfig
public class AppConfig {
@Bean
public SampleRedisConfig sampleRedisConfig() {
return new SampleRedisConfig();
}
}
需要注意的是,@ConfigurationProperties
如果需要在Apollo配置變化時自動更新注入的值,需要配合Spring Cloud的RefreshScope使用。相關程式碼實現,可以參考apollo-demo專案中的SampleRedisConfig.java和SpringBootApolloRefreshConfig.java
3.2.3 Spring Annotation支援
Apollo同時還增加了兩個新的Annotation來簡化在Spring環境中的使用。
- @ApolloConfig
- 用來自動注入Config物件
- @ApolloConfigChangeListener
- 用來自動註冊ConfigChangeListener
使用樣例如下:
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
@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;
}
}
在Configuration類中按照下面的方式使用:
@Configuration
@EnableApolloConfig
public class AppConfig {
@Bean
public TestApolloAnnotationBean testApolloAnnotationBean() {
return new TestApolloAnnotationBean();
}
}
3.3 Demo
專案中有一個樣例客戶端的專案:apollo-demo
,具體資訊可以參考Apollo開發指南中的2.3 Java樣例客戶端啟動部分。
四、客戶端設計
上圖簡要描述了Apollo客戶端的實現原理:
- 客戶端和服務端保持了一個長連線,從而能第一時間獲得配置更新的推送。(通過Http Long Polling實現)
- 客戶端還會定時從Apollo配置中心服務端拉取應用的最新配置。
- 這是一個fallback機制,為了防止推送機制失效導致配置不更新
- 客戶端定時拉取會上報本地版本,所以一般情況下,對於定時拉取的操作,服務端都會返回304 - Not Modified
- 定時頻率預設為每5分鐘拉取一次,客戶端也可以通過在執行時指定System Property:
apollo.refreshInterval
來覆蓋,單位為分鐘。
- 客戶端從Apollo配置中心服務端獲取到應用的最新配置後,會儲存在記憶體中
- 客戶端會把從服務端獲取到的配置在本地檔案系統快取一份
- 在遇到服務不可用,或網路不通的時候,依然能從本地恢復配置
- 應用程式可以從Apollo客戶端獲取最新的配置、訂閱配置更新通知
五、本地開發模式
Apollo客戶端還支援本地開發模式,這個主要用於當開發環境無法連線Apollo伺服器的時候,比如在郵輪、飛機上做相關功能開發。
在本地開發模式下,Apollo只會從本地檔案讀取配置資訊,不會從Apollo伺服器讀取配置。
可以通過下面的步驟開啟Apollo本地開發模式。
5.1 修改環境
修改/opt/settings/server.properties(Mac/Linux)或C:\opt\settings\server.properties(Windows)檔案,設定env為Local:
env=Local
5.2 準備本地配置檔案
在本地開發模式下,Apollo客戶端會從本地讀取檔案,所以我們需要事先準備好配置檔案。
5.2.1 本地配置目錄
本地配置目錄位於:
- Mac/Linux: /opt/data/{appId}/config-cache
- Windows: C:\opt\data\{appId}\config-cache
appId就是應用的appId,如100004458。
請確保該目錄存在,且應用程式對該目錄有讀許可權。
【小技巧】 推薦的方式是先在普通模式下使用Apollo,這樣Apollo會自動建立該目錄並在目錄下生成配置檔案。
5.2.2 本地配置檔案
本地配置檔案需要按照一定的檔名格式放置於本地配置目錄下,檔名格式如下:
{appId}+{cluster}+{namespace}.properties
- appId就是應用自己的appId,如100004458
- cluster就是應用使用的叢集,一般在本地模式下沒有做過配置的話,就是default
- namespace就是應用使用的配置namespace,一般是application
檔案內容以properties格式儲存,比如如果有兩個key,一個是request.timeout,另一個是batch,那麼檔案內容就是如下格式:
request.timeout=2000
batch=2000
5.3 修改配置
在本地開發模式下,Apollo不會實時監測檔案內容是否有變化,所以如果修改了配置,需要重啟應用生效。