攜程Apollo統一配置中心的搭建和使用(java)

https://blog.csdn.net/luhong327/article/details/81453001#commentBox

一.Apollo配置中心介紹
1、What is Apollo
1.1 Apollo簡介
Apollo(阿波羅)是攜程框架部門研發的開源配置管理中心,能夠集中化管理應用不同環境、不同集羣的配置,配置修改後能夠實時推送到應用端,並且具備規範的權限、流程治理等特性。

Apollo支持4個維度管理Key-Value格式的配置:

application (應用)
environment (環境)
cluster (集羣)
namespace (命名空間)
同時,Apollo基於開源模式開發,開源地址:https://github.com/ctripcorp/apollo

 
 二、分佈式部署指南
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環境(迴歸測試)
PRO
 

生產環境
自定義環境,參考部署&開發遇到的常見問題#42-添加自定義的環境

 

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服務端
獲取安裝包後就可以部署到公司的測試和生產環境了
2.1 創建數據庫
Apollo服務端共需要兩個數據庫:ApolloPortalDB和ApolloConfigDB

2.1.1 創建ApolloPortalDB
sql:下載地址:https://github.com/ctripcorp/apollo/tree/master/scripts/sql

通過各種MySQL客戶端導入sql/apolloportaldb.sql即可。

導入成功後,可以通過執行以下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:下載地址:https://github.com/ctripcorp/apollo/tree/master/scripts/sql

通過各種MySQL客戶端導入sql/apolloconfigdb.sql即可。

導入成功後,可以通過執行以下sql語句來驗證:

select `Id`, `Key`, `Value`, `Comment` from `ApolloConfigDB`.`ServerConfig` limit 1;
Id    Key    Value    Comment
1    eureka.service.url    http://127.0.0.1:8080/eureka/    Eureka服務Url
注: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:

修改com.ctrip.framework.apollo.configservice.ConfigServiceApplication,把@EnableEurekaServer改爲@EnableEurekaClient
@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 獲取安裝包
可以通過兩種方式獲取安裝包:

直接下載安裝包
從GitHub Release頁面下載預先打好的安裝包
GitHub地址:https://github.com/ctripcorp/apollo/releases
如果對Apollo的代碼沒有定製需求,建議使用這種方式,可以省去本地打包的過程
測試V0.11.0版本,在下載安裝包,成功部署後,後臺默認賬號密碼會出現密碼錯誤,後來使用了源碼構建
通過源碼構建
從GitHub Release頁面下載Source code包或直接clone源碼後在本地構建
GitHub地址:https://github.com/ctripcorp/apollo/releases
如果需要對Apollo的做定製開發,需要使用這種方式
2.2.1 直接下載安裝包
下載安裝包,這裏就不說了,參考:apollo官網配置文件

https://github.com/ctripcorp/apollo/wiki/%E5%88%86%E5%B8%83%E5%BC%8F%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97

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
注意key爲全小寫
通過操作系統的System Environment

還可以通過操作系統的System Environment ENV來指定
注意key爲全大寫
通過配置文件

最後一個推薦的方式是通過配置文件來指定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
通過配置文件

首先確保/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;其它方法請參考官方文檔

https://github.com/ctripcorp/apollo/wiki/Java%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97

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)的方法。
如果要刷新的不是自己的bean,而是spring cloud的bean,如spring cloud zuul,那麼可以調用org.springframework.cloud.context.refresh.ContextRefresher#refresh來刷新配置
相關代碼實現,可以參考apollo-demo項目中的AnnotatedBean.java和ApolloRefreshConfig.java
在非Spring Boot環境使用RefreshScope需要引入RefreshAutoConfiguration,引入方式可以參考apollo-demo項目中的相關配置代碼:JavaConfig示例,XmlConfig示例
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使用方式

Spring Boot提供了@ConfigurationProperties把配置注入到bean對象中。

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樣例客戶端啓動部分。

更多使用案例Demo可以參考Apollo使用場景和示例代碼。

四、客戶端設計


上圖簡要描述了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
更多配置環境的方式請參考1.2.2 Environment

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不會實時監測文件內容是否有變化,所以如果修改了配置,需要重啓應用生效。

 

 

 

參考文檔:https://github.com/ctripcorp/apollo
--------------------- 
作者:luhong327 
來源:CSDN 
原文:https://blog.csdn.net/luhong327/article/details/81453001 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

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