• 携程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:optsettingsserver.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:optdata目录存在,且应用有读写权限。

        Mac/Linux: /opt/data/{appId}/config-cache
        Windows: C:optdata{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:optsettingsserver.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:optsettingsserver.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:optdata{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

  • 相关阅读:
    Linux下Apache服务器并发优化
    centos 7 mount win共享文件夹 开机自动挂载
    自学 phpredis 的心路历程
    VM虚拟机下centos7 无法上网的问题解决办法
    php headers_sent 函数的作用
    is_file 与 file_exists 的区别
    php 面向对象 中的self
    php 去除所有空格 包括中文空格圆角空格
    滑动窗口滚动条触发事件
    PHP中file_exists与is_file、is_dir的区别,以及执行效率的比较 转自#冰雪傲骨#
  • 原文地址:https://www.cnblogs.com/yx88/p/12261420.html
Copyright © 2020-2023  润新知