1. 程式人生 > >攜程Apollo統一配置中心的搭建和使用(java)

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

一.Apollo配置中心介紹

1、What is Apollo

1.1 Apollo簡介

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

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

  1. application (應用)
  2. environment (環境)
  3. cluster (叢集)
  4. 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、部署步驟

部署步驟共三步:

  1. 建立資料庫

    ApolloconfigDB(), 

    • Apollo服務端依賴於MySQL資料庫,所以需要事先建立並完成初始化
  2. 獲取安裝包:GitHub地址:https://github.com/ctripcorp/apollo/releases
    • Apollo服務端安裝包共有3個:apollo-configservice, apollo-adminservice, apollo-portal
      • 可以直接下載我們事先打好的安裝包,也可以自己通過原始碼構建  
      • Apollo客戶端jar包中由於會包含meta server資訊,無法上傳一個統一的jar包到中央倉庫
      • 可以直接下載我們事先打好的安裝包,修改相應配置後上傳到自己公司的Maven私服
      • 也可以直接通過原始碼構建並上傳到公司的Maven私服
  3. 部署Apollo服務端
    • 獲取安裝包後就可以部署到公司的測試和生產環境了

2.1 建立資料庫

Apollo服務端共需要兩個資料庫:ApolloPortalDBApolloConfigDB

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/字尾)。

如:內網開發環境

  1. 在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 {
  ...
}
  1. 修改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 獲取安裝包

可以通過兩種方式獲取安裝包:

  1. 直接下載安裝包
    • 如果對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.reposnapshots.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相關檔案都上傳。

apollo-client-maven-artifacts

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客戶端依賴於AppIdEnvironment等環境資訊來工作,所以請確保閱讀下面的說明並且做正確的配置:

1.2.1 AppId

AppId是應用的身份資訊,是從服務端獲取配置的一個重要資訊。

請確保classpath:/META-INF/app.properties檔案存在,並且其中內容形如:

app.id=YOUR-APP-ID

檔案位置參考如下:

app-id-location

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種方式的任意一個配置:

  1. 通過Java System Property

    • 可以通過Java的System Property env來指定環境
    • 在Java程式啟動指令碼中,可以指定-Denv=YOUR-ENVIRONMENT
      • 如果是執行jar檔案,需要注意格式是java -Denv=YOUR-ENVIRONMENT -jar xxx.jar
    • 注意key為全小寫
  2. 通過作業系統的System Environment

    • 還可以通過作業系統的System Environment ENV來指定
    • 注意key為全大寫
  3. 通過配置檔案

    • 最後一個推薦的方式是通過配置檔案來指定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/dataC:\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 client-local-cache

檔案內容以properties格式儲存,比如如果有兩個key,一個是request.timeout,另一個是batch,那麼檔案內容就是如下格式:

request.timeout=2000
batch=2000

注:本地快取路徑也可用於容災目錄,如果應用在所有config service都掛掉的情況下需要擴容,那麼也可以先把配置從已有機器上的快取路徑複製到新機器上的相同快取路徑

1.2.4 可選設定

Cluster(叢集)

Apollo支援配置按照叢集劃分,也就是說對於一個appId和一個環境,對不同的叢集可以有不同的配置。

如果需要使用這個功能,你可以通過以下方式來指定執行時的叢集:

  1. 通過Java System Property

    • 我們可以通過Java的System Property設定apollo.cluster來指定執行時叢集(注意key為全小寫)
    • 例如,可以在程式啟動時通過-Dapollo.cluster=SomeCluster來指定執行時的叢集為SomeCluster
  2. 通過配置檔案

    • 首先確保/opt/settings/server.properties(Mac/Linux)或C:\opt\settings\server.properties(Windows)在目標機器上存在
    • 在這個檔案中,可以設定資料中心叢集,如idc=xxx
    • 注意key為全小寫

Cluster Precedence(叢集順序)

  1. 如果apollo.clusteridc同時指定:

    • 我們會首先嚐試從apollo.cluster指定的叢集載入配置
    • 如果沒找到任何配置,會嘗試從idc指定的叢集載入配置
    • 如果還是沒找到,會從預設的叢集(default)載入
  2. 如果只指定了apollo.cluster

    • 我們會首先嚐試從apollo.cluster指定的叢集載入配置
    • 如果沒找到,會從預設的叢集(default)載入
  3. 如果只指定了idc

    • 我們會首先嚐試從idc指定的叢集載入配置
    • 如果沒找到,會從預設的叢集(default)載入
  4. 如果apollo.clusteridc都沒有指定:

    • 我們會從預設的叢集(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中按照如下樣例配置即可。

  1. 在bootstrap階段注入預設application namespace的配置示例
     # will inject 'application' namespace in bootstrap phase
     apollo.bootstrap.enabled = true
  1. 在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在執行時自動更新功能,可以通過以下兩種方式關閉:

  1. 通過設定System Property apollo.autoUpdateInjectedSpringProperties,如啟動時傳入-Dapollo.autoUpdateInjectedSpringProperties=false

  2. 通過設定META-INF/app.properties中的apollo.autoUpdateInjectedSpringProperties屬性,如

app.id=SampleApp
apollo.autoUpdateInjectedSpringProperties=false

對於v0.10.0之前的版本,placeholder只會在啟動的時候賦值,在執行過程中即便在Apollo上修改釋出了配置,placeholder的值也不會更新。如果需要執行時動態更新placeholder的值,有以下幾種方式:

  1. 程式監聽Apollo的ConfigChangeListener(如@ApolloConfigChangeListener),然後通過自己的程式碼來更新placeholder的值。
  2. 不使用placeholder方式獲取配置,而是直接從config物件中獲取配置。樣例程式碼可以參考3.2.3 Spring Annotation支援getBatchgetTimeout方法。
  3. 【推薦】使用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中有timeoutbatch的配置項):

@Configuration
@EnableApolloConfig
public class AppConfig {
  @Bean
  public TestJavaConfigBean javaConfigBean() {
    return new TestJavaConfigBean();
  }
}

3.2.2.3 ConfigurationProperties使用方式

Apollo也支援這種方式,下面的例子會把redis.cache.expireSecondsredis.cache.commandTimeout分別注入到SampleRedisConfig的expireSecondscommandTimeout欄位中。

@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.expireSecondsredis.cache.commandTimeout的配置項):

@Configuration
@EnableApolloConfig
public class AppConfig {
  @Bean
  public SampleRedisConfig sampleRedisConfig() {
    return new SampleRedisConfig();
  }
}

需要注意的是,@ConfigurationProperties如果需要在Apollo配置變化時自動更新注入的值,需要配合Spring Cloud的RefreshScope使用。相關程式碼實現,可以參考apollo-demo專案中的SampleRedisConfig.javaSpringBootApolloRefreshConfig.java

3.2.3 Spring Annotation支援

Apollo同時還增加了兩個新的Annotation來簡化在Spring環境中的使用。

  1. @ApolloConfig
    • 用來自動注入Config物件
  2. @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();
  }
}

text-mode-spring-boot-config-sample

3.3 Demo

專案中有一個樣例客戶端的專案:apollo-demo,具體資訊可以參考Apollo開發指南中的2.3 Java樣例客戶端啟動部分。

四、客戶端設計

client-architecture

上圖簡要描述了Apollo客戶端的實現原理:

  1. 客戶端和服務端保持了一個長連線,從而能第一時間獲得配置更新的推送。(通過Http Long Polling實現)
  2. 客戶端還會定時從Apollo配置中心服務端拉取應用的最新配置。
    • 這是一個fallback機制,為了防止推送機制失效導致配置不更新
    • 客戶端定時拉取會上報本地版本,所以一般情況下,對於定時拉取的操作,服務端都會返回304 - Not Modified
    • 定時頻率預設為每5分鐘拉取一次,客戶端也可以通過在執行時指定System Property: apollo.refreshInterval來覆蓋,單位為分鐘。
  3. 客戶端從Apollo配置中心服務端獲取到應用的最新配置後,會儲存在記憶體中
  4. 客戶端會把從服務端獲取到的配置在本地檔案系統快取一份
    • 在遇到服務不可用,或網路不通的時候,依然能從本地恢復配置
  5. 應用程式可以從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 client-local-cache

檔案內容以properties格式儲存,比如如果有兩個key,一個是request.timeout,另一個是batch,那麼檔案內容就是如下格式:

request.timeout=2000
batch=2000

5.3 修改配置

在本地開發模式下,Apollo不會實時監測檔案內容是否有變化,所以如果修改了配置,需要重啟應用生效。