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

一.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相关文件都上传。

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客户端依赖于AppId,Environment等环境信息来工作,所以请确保阅读下面的说明并且做正确的配置:
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种方式的任意一个配置:

    通过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 client-local-cache

文件内容以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();
      }
    }

text-mode-spring-boot-config-sample
3.3 Demo

项目中有一个样例客户端的项目:apollo-demo,具体信息可以参考Apollo开发指南中的2.3 Java样例客户端启动部分。

更多使用案例Demo可以参考Apollo使用场景和示例代码。
四、客户端设计

client-architecture

上图简要描述了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 client-local-cache

文件内容以properties格式存储,比如如果有两个key,一个是request.timeout,另一个是batch,那么文件内容就是如下格式:

    request.timeout=2000
    batch=2000

5.3 修改配置

在本地开发模式下,Apollo不会实时监测文件内容是否有变化,所以如果修改了配置,需要重启应用生效。

 


————————————————
版权声明:本文为CSDN博主「故人何谓」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luhong327/article/details/81453001

posted @ 2020-02-04 21:10  学习者_3988  阅读(1401)  评论(0编辑  收藏  举报