Apollo(阿波罗)配置中心Java客户端使用指南使用指南

      Apollo(阿波罗)是携程框架部门研发的分布式配置中心,可以集中化管理应用不一样环境、不一样集群的配置,配置修改后可以实时推送到应用端,而且具有规范的权限、流程治理等特性, 适用于微服务配置管理场景。
 

1、准备工做html

1.1 环境要求

  • Java: 1.7+
  • Guava: 15.0+
    • Apollo客户端默认会引用Guava 19,若是你的项目引用了其它版本,请确保版本号大于等于15.0

注:对于Apollo客户端,若是有须要的话,能够作少许代码修改来降级到Java 1.6,详细信息能够参考Issue 483java

1.2 必选设置

Apollo客户端依赖于AppIdApollo Meta Server等环境信息来工做,因此请确保阅读下面的说明而且作正确的配置:mysql

1.2.1 AppId

AppId是应用的身份信息,是从服务端获取配置的一个重要信息。git

有如下几种方式设置,按照优先级从高到低分别为:github

  1. System Property

Apollo 0.7.0+支持经过System Property传入app.id信息,如redis

-Dapp.id=YOUR-APP-ID
  1. 操做系统的System Environment

Apollo 1.4.0+支持经过操做系统的System Environment APP_ID来传入app.id信息,如spring

APP_ID=YOUR-APP-ID
  1. Spring Boot application.properties

Apollo 1.0.0+支持经过Spring Boot的application.properties文件配置,如sql

app.id=YOUR-APP-ID

该配置方式不适用于多个war包部署在同一个tomcat的使用场景docker

  1. app.properties

确保classpath:/META-INF/app.properties文件存在,而且其中内容形如:shell

app.id=YOUR-APP-ID

文件位置参考以下:

app-id-location

注:app.id是用来标识应用身份的惟一id,格式为string。

1.2.2 Apollo Meta Server

Apollo支持应用在不一样的环境有不一样的配置,因此须要在运行提供给Apollo客户端当前环境的Apollo Meta Server信息。默认状况下,meta server和config service是部署在同一个JVM进程,因此meta server的地址就是config service的地址。

为了实现meta server的高可用,推荐经过SLB(Software Load Balancer)作动态负载均衡。Meta server地址也能够填入IP,如http://1.1.1.1:8080,http://2.2.2.2:8080,不过生产环境仍是建议使用域名(走slb),由于机器扩容、缩容等均可能致使IP列表的变化。

1.0.0版本开始支持如下方式配置apollo meta server信息,按照优先级从高到低分别为:

  1. 经过Java System Property apollo.meta
    • 能够经过Java的System Property apollo.meta来指定
    • 在Java程序启动脚本中,能够指定-Dapollo.meta=http://config-service-url
      • 若是是运行jar文件,须要注意格式是java -Dapollo.meta=http://config-service-url -jar xxx.jar
    • 也能够经过程序指定,如System.setProperty("apollo.meta", "http://config-service-url");
  2. 经过Spring Boot的配置文件
    • 能够在Spring Boot的application.propertiesbootstrap.properties中指定apollo.meta=http://config-service-url

该配置方式不适用于多个war包部署在同一个tomcat的使用场景

  1. 经过操做系统的System EnvironmentAPOLLO_META
    • 能够经过操做系统的System Environment APOLLO_META来指定
    • 注意key为全大写,且中间是_分隔
  2. 经过server.properties配置文件
    • 能够在server.properties配置文件中指定apollo.meta=http://config-service-url
    • 对于Mac/Linux,文件位置为/opt/settings/server.properties
    • 对于Windows,文件位置为C:\opt\settings\server.properties
  3. 经过app.properties配置文件
    • 能够在classpath:/META-INF/app.properties指定apollo.meta=http://config-service-url
  4. 经过Java system property ${env}_meta
    • 若是当前envdev,那么用户能够配置-Ddev_meta=http://config-service-url
    • 使用该配置方式,那么就必需要正确配置Environment,详见1.2.4.1 Environment
  5. 经过操做系统的System Environment ${ENV}_META (1.2.0版本开始支持)
    • 若是当前envdev,那么用户能够配置操做系统的System Environment DEV_META=http://config-service-url
    • 注意key为全大写
    • 使用该配置方式,那么就必需要正确配置Environment,详见1.2.4.1 Environment
  6. 经过apollo-env.properties文件
    • 用户也能够建立一个apollo-env.properties,放在程序的classpath下,或者放在spring boot应用的config目录下
    • 使用该配置方式,那么就必需要正确配置Environment,详见1.2.4.1 Environment
    • 文件内容形如:
dev.meta=http://1.1.1.1:8080 fat.meta=http://apollo.fat.xxx.com uat.meta=http://apollo.uat.xxx.com pro.meta=http://apollo.xxx.com

若是经过以上各类手段都没法获取到Meta Server地址,Apollo最终会fallback到http://apollo.meta做为Meta Server地址

1.2.2.1 自定义Apollo Meta Server地址定位逻辑

在1.0.0版本中,Apollo提供了MetaServerProvider SPI,用户能够注入本身的MetaServerProvider来自定义Meta Server地址定位逻辑。

因为咱们使用典型的Java Service Loader模式,因此实现起来仍是比较简单的。

有一点须要注意的是,apollo会在运行时按照顺序遍历全部的MetaServerProvider,直到某一个MetaServerProvider提供了一个非空的Meta Server地址,所以用户须要格外注意自定义MetaServerProvider的Order。规则是较小的Order具备较高的优先级,所以Order=0的MetaServerProvider会排在Order=1的MetaServerProvider的前面。

若是你的公司有不少应用须要接入Apollo,建议封装一个jar包,而后提供自定义的Apollo Meta Server定位逻辑,从而可让接入Apollo的应用零配置使用。好比本身写一个xx-company-apollo-client,该jar包依赖apollo-client,在该jar包中经过spi方式定义自定义的MetaServerProvider实现,而后应用直接依赖xx-company-apollo-client便可。

MetaServerProvider的实现能够参考LegacyMetaServerProviderDefaultMetaServerProvider

1.2.2.2 跳过Apollo Meta Server服务发现

适用于apollo-client 0.11.0及以上版本

通常状况下都建议使用Apollo的Meta Server机制来实现Config Service的服务发现,从而能够实现Config Service的高可用。不过apollo-client也支持跳过Meta Server服务发现,主要用于如下场景:

  1. Config Service部署在公有云上,注册到Meta Server的是内网地址,本地开发环境没法直接链接
  2. Config Service部署在docker环境中,注册到Meta Server的是docker内网地址,本地开发环境没法直接链接
  3. Config Service部署在kubernetes中,但愿使用kubernetes自带的服务发现能力(Service)

针对以上场景,能够经过直接指定Config Service地址的方式来跳过Meta Server服务发现,按照优先级从高到低分别为:

  1. 经过Java System Property apollo.configService
    • 能够经过Java的System Property apollo.configService来指定
    • 在Java程序启动脚本中,能够指定-Dapollo.configService=http://config-service-url:port
      • 若是是运行jar文件,须要注意格式是java -Dapollo.configService=http://config-service-url:port -jar xxx.jar
    • 也能够经过程序指定,如System.setProperty("apollo.configService", "http://config-service-url:port");
  2. 经过操做系统的System EnvironmentAPOLLO_CONFIGSERVICE
    • 能够经过操做系统的System Environment APOLLO_CONFIGSERVICE来指定
    • 注意key为全大写,且中间是_分隔
  3. 经过server.properties配置文件
    • 能够在server.properties配置文件中指定apollo.configService=http://config-service-url:port
    • 对于Mac/Linux,文件位置为/opt/settings/server.properties
    • 对于Windows,文件位置为C:\opt\settings\server.properties

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

1.2.3.1 自定义缓存路径

1.0.0版本开始支持如下方式自定义缓存路径,按照优先级从高到低分别为:

  1. 经过Java System Property apollo.cacheDir
    • 能够经过Java的System Property apollo.cacheDir来指定
    • 在Java程序启动脚本中,能够指定-Dapollo.cacheDir=/opt/data/some-cache-dir
      • 若是是运行jar文件,须要注意格式是java -Dapollo.cacheDir=/opt/data/some-cache-dir -jar xxx.jar
    • 也能够经过程序指定,如System.setProperty("apollo.cacheDir", "/opt/data/some-cache-dir");
  2. 经过Spring Boot的配置文件
    • 能够在Spring Boot的application.propertiesbootstrap.properties中指定apollo.cacheDir=/opt/data/some-cache-dir
  3. 经过操做系统的System EnvironmentAPOLLO_CACHEDIR
    • 能够经过操做系统的System Environment APOLLO_CACHEDIR来指定
    • 注意key为全大写,且中间是_分隔
  4. 经过server.properties配置文件
    • 能够在server.properties配置文件中指定apollo.cacheDir=/opt/data/some-cache-dir
    • 对于Mac/Linux,文件位置为/opt/settings/server.properties
    • 对于Windows,文件位置为C:\opt\settings\server.properties

注:本地缓存路径也可用于容灾目录,若是应用在全部config service都挂掉的状况下须要扩容,那么也能够先把配置从已有机器上的缓存路径复制到新机器上的相同缓存路径

1.2.4 可选设置

1.2.4.1 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

更多环境定义,能够参考Env.java

1.2.4.2 Cluster(集群)

Apollo支持配置按照集群划分,也就是说对于一个appId和一个环境,对不一样的集群能够有不一样的配置。

1.0.0版本开始支持如下方式集群,按照优先级从高到低分别为:

  1. 经过Java System Property apollo.cluster
    • 能够经过Java的System Property apollo.cluster来指定
    • 在Java程序启动脚本中,能够指定-Dapollo.cluster=SomeCluster
      • 若是是运行jar文件,须要注意格式是java -Dapollo.cluster=SomeCluster -jar xxx.jar
    • 也能够经过程序指定,如System.setProperty("apollo.cluster", "SomeCluster");
  2. 经过Spring Boot的配置文件
    • 能够在Spring Boot的application.propertiesbootstrap.properties中指定apollo.cluster=SomeCluster
  3. 经过Java System Property
    • 能够经过Java的System Property idc来指定环境
    • 在Java程序启动脚本中,能够指定-Didc=xxx
      • 若是是运行jar文件,须要注意格式是java -Didc=xxx -jar xxx.jar
    • 注意key为全小写
  4. 经过操做系统的System Environment
    • 还能够经过操做系统的System Environment IDC来指定
    • 注意key为全大写
  5. 经过server.properties配置文件
    • 能够在server.properties配置文件中指定idc=xxx
    • 对于Mac/Linux,文件位置为/opt/settings/server.properties
    • 对于Windows,文件位置为C:\opt\settings\server.properties

Cluster Precedence(集群顺序)

  1. 若是apollo.clusteridc同时指定:

    • 咱们会首先尝试从apollo.cluster指定的集群加载配置
    • 若是没找到任何配置,会尝试从idc指定的集群加载配置
    • 若是仍是没找到,会从默认的集群(default)加载
  2. 若是只指定了apollo.cluster

    • 咱们会首先尝试从apollo.cluster指定的集群加载配置
    • 若是没找到,会从默认的集群(default)加载
  3. 若是只指定了idc

    • 咱们会首先尝试从idc指定的集群加载配置
    • 若是没找到,会从默认的集群(default)加载
  4. 若是apollo.clusteridc都没有指定:

    • 咱们会从默认的集群(default)加载配置

1.2.4.3 设置内存中的配置项是否保持和页面上的顺序一致

适用于1.6.0及以上版本

默认状况下,apollo client内存中的配置存放在Properties中(底下是Hashtable),不会刻意保持和页面上看到的顺序一致,对绝大部分的场景是没有影响的。不过有些场景会强依赖配置项的顺序(如spring cloud zuul的路由规则),针对这种状况,能够开启OrderedProperties特性来使得内存中的配置顺序和页面上看到的一致。

配置方式按照优先级从高到低分别为:

  1. 经过Java System Property apollo.property.order.enable
    • 能够经过Java的System Property apollo.property.order.enable来指定
    • 在Java程序启动脚本中,能够指定-Dapollo.property.order.enable=true
      • 若是是运行jar文件,须要注意格式是java -Dapollo.property.order.enable=true -jar xxx.jar
    • 也能够经过程序指定,如System.setProperty("apollo.property.order.enable", "true");
  2. 经过Spring Boot的配置文件
    • 能够在Spring Boot的application.propertiesbootstrap.properties中指定apollo.property.order.enable=true
  3. 经过app.properties配置文件
    • 能够在classpath:/META-INF/app.properties指定apollo.property.order.enable=true

2、Maven Dependency

Apollo的客户端jar包已经上传到中央仓库,应用在实际使用时只须要按照以下方式引入便可。

<dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.1.0</version> </dependency>

3、客户端用法

Apollo支持API方式和Spring整合方式,该怎么选择用哪种方式?

  • API方式灵活,功能完备,配置值实时更新(热发布),支持全部Java环境。
  • Spring方式接入简单,结合Spring有N种酷炫的玩法,如
    • Placeholder方式:
      • 代码中直接使用,如:@Value("${someKeyFromApollo:someDefaultValue}")
      • 配置文件中使用替换placeholder,如:spring.datasource.url: ${someKeyFromApollo:someDefaultValue}
      • 直接托管spring的配置,如在apollo中直接配置spring.datasource.url=jdbc:mysql://localhost:3306/somedb?characterEncoding=utf8
    • Spring boot的@ConfigurationProperties方式
    • 从v0.10.0开始的版本支持placeholder在运行时自动更新,具体参见PR #972。(v0.10.0以前的版本在配置变化后不会从新注入,须要重启才会更新,若是须要配置值实时更新,能够参考后续3.2.2 Spring Placeholder的使用的说明)
  • Spring方式也能够结合API方式使用,如注入Apollo的Config对象,就能够照常经过API方式获取配置了:
    @ApolloConfig private Config config; //inject config for namespace application
  • 更多有意思的实际使用场景和示例代码,请参考apollo-use-cases

3.1 API使用方式

API方式是最简单、高效使用Apollo配置的方式,不依赖Spring框架便可使用。

3.1.1 获取默认namespace的配置(application)

Config config = ConfigService.getAppConfig(); //config instance is singleton for each namespace and is never null String someKey = "someKeyFromDefaultNamespace"; String someDefaultValue = "someDefaultValueForTheKey"; String value = config.getProperty(someKey, someDefaultValue);

经过上述的config.getProperty能够获取到someKey对应的实时最新的配置值。

另外,配置值从内存中获取,因此不须要应用本身作缓存。

3.1.2 监听配置变化事件

监听配置变化事件只在应用真的关心配置变化,须要在配置变化时获得通知时使用,好比:数据库链接串变化后须要重建链接等。

若是只是但愿每次都取到最新的配置的话,只须要按照上面的例子,调用config.getProperty便可。

Config config = ConfigService.getAppConfig(); //config instance is singleton for each namespace and is never null config.addChangeListener(new ConfigChangeListener() { @Override public void onChange(ConfigChangeEvent changeEvent) { System.out.println("Changes for namespace " + changeEvent.getNamespace()); for (String key : changeEvent.changedKeys()) { ConfigChange change = changeEvent.getChange(key); System.out.println(String.format("Found change - key: %s, oldValue: %s, newValue: %s, changeType: %s", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType())); } } });

3.1.3 获取公共Namespace的配置

String somePublicNamespace = "CAT"; Config config = ConfigService.getConfig(somePublicNamespace); //config instance is singleton for each namespace and is never null String someKey = "someKeyFromPublicNamespace"; String someDefaultValue = "someDefaultValueForTheKey"; String value = config.getProperty(someKey, someDefaultValue);

3.1.4 获取非properties格式namespace的配置

3.1.4.1 yaml/yml格式的namespace

apollo-client 1.3.0版本开始对yaml/yml作了更好的支持,使用起来和properties格式一致。

Config config = ConfigService.getConfig("application.yml"); String someKey = "someKeyFromYmlNamespace"; String someDefaultValue = "someDefaultValueForTheKey"; String value = config.getProperty(someKey, someDefaultValue);

3.1.4.2 非yaml/yml格式的namespace

获取时须要使用ConfigService.getConfigFile接口并指定Format,如ConfigFileFormat.XML

String someNamespace = "test"; ConfigFile configFile = ConfigService.getConfigFile("test", ConfigFileFormat.XML); String content = configFile.getContent();

3.2 Spring整合方式

3.2.1 配置

Apollo也支持和Spring整合(Spring 3.1.1+),只须要作一些简单的配置就能够了。

Apollo目前既支持比较传统的基于XML的配置,也支持目前比较流行的基于Java(推荐)的配置。

若是是Spring Boot环境,建议参照3.2.1.3 Spring Boot集成方式(推荐)配置。

须要注意的是,若是以前有使用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

注1:yaml/yml格式的namespace从1.3.0版本开始支持和Spring整合,注入时须要填写带后缀的完整名字,好比application.yml

注2:非properties、非yaml/yml格式(如xml,json等)的namespace暂不支持和Spring整合。

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和application.yml namespace的配置到Spring环境中 --> <apollo:config namespaces="FX.apollo,application.yml"/> <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和application.yml namespace的配置到Spring环境中,而且顺序在application前面 --> <apollo:config namespaces="FX.apollo,application.yml" 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和application.yml namespace的配置到Spring环境中 @Configuration @EnableApolloConfig({"FX.apollo", "application.yml"}) public class AnotherAppConfig {}

3.注入多个namespace,而且指定顺序

//这个是最复杂的配置形式,指示Apollo注入FX.apollo和application.yml namespace的配置到Spring环境中,而且顺序在application前面 @Configuration @EnableApolloConfig(order = 2) public class SomeAppConfig { @Bean public TestJavaConfigBean javaConfigBean() { return new TestJavaConfigBean(); } } @Configuration @EnableApolloConfig(value = {"FX.apollo", "application.yml"}, order = 1) public class AnotherAppConfig {}

3.2.1.3 Spring Boot集成方式(推荐)

Spring Boot除了支持上述两种集成方式之外,还支持经过application.properties/bootstrap.properties来配置,该方式能使配置在更早的阶段注入,好比使用@ConditionalOnProperty的场景或者是有一些spring-boot-starter在启动阶段就须要读取配置作一些事情(如dubbo-spring-boot-project),因此对于Spring Boot环境建议经过如下方式来接入Apollo(须要0.10.0及以上版本)。

使用方式很简单,只须要在application.properties/bootstrap.properties中按照以下样例配置便可。

  1. 注入默认application namespace的配置示例
# will inject 'application' namespace in bootstrap phase apollo.bootstrap.enabled = true
  1. 注入非默认application namespace或多个namespace的配置示例
apollo.bootstrap.enabled = true # will inject 'application', 'FX.apollo' and 'application.yml' namespaces in bootstrap phase apollo.bootstrap.namespaces = application,FX.apollo,application.yml
  1. 将Apollo配置加载提到初始化日志系统以前(1.2.0+)

从1.2.0版本开始,若是但愿把日志相关的配置(如logging.level.root=infologback-spring.xml中的参数)也放在Apollo管理,那么能够额外配置apollo.bootstrap.eagerLoad.enabled=true来使Apollo的加载顺序放到日志系统加载以前,不过这会致使Apollo的启动过程没法经过日志的方式输出(由于执行Apollo加载的时候,日志系统压根没有准备好呢!因此在Apollo代码中使用Slf4j的日志输出便没有任何内容),更多信息能够参考PR 1614。参考配置示例以下:

# will inject 'application' namespace in bootstrap phase apollo.bootstrap.enabled = true # put apollo initialization before logging system initialization apollo.bootstrap.eagerLoad.enabled=true

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

3.2.2.1 XML使用方式

假设我有一个TestXmlBean,它有两个配置项须要注入:

public class TestXmlBean { private int timeout; private int batch; public void setTimeout(int timeout) { this.timeout = timeout; } public void setBatch(int batch) { this.batch = batch; } public int getTimeout() { return timeout; } public int getBatch() { return batch; } }

那么,我在XML中会使用以下方式来定义(假设应用默认的application namespace中有timeout和batch的配置项):

<?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/> <bean class="com.ctrip.framework.apollo.spring.TestXmlBean"> <property name="timeout" value="${timeout:100}"/> <property name="batch" value="${batch:200}"/> </bean> </beans>

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使用方式

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

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配置变化时自动更新注入的值,须要配合使用EnvironmentChangeEventRefreshScope。相关代码实现,能够参考apollo-use-cases项目中的ZuulPropertiesRefresher.java和apollo-demo项目中的SampleRedisConfig.java以及SpringBootApolloRefreshConfig.java

3.2.3 Spring Annotation支持

Apollo同时还增长了几个新的Annotation来简化在Spring环境中的使用。

  1. @ApolloConfig
    • 用来自动注入Config对象
  2. @ApolloConfigChangeListener
    • 用来自动注册ConfigChangeListener
  3. @ApolloJsonValue
    • 用来把配置的json字符串自动注入为对象

使用样例以下:

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 @ApolloConfig("application.yml") private Config ymlConfig; //inject config for namespace application.yml /**  * ApolloJsonValue annotated on fields example, the default value is specified as empty list - []  * <br />  * jsonBeanProperty=[{"someString":"hello","someInt":100},{"someString":"world!","someInt":200}]  */ @ApolloJsonValue("${jsonBeanProperty:[]}") private List<JsonBean> anotherJsonBeans; @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, FX.apollo and application.yml @ApolloConfigChangeListener({"application", "FX.apollo", "application.yml"}) 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; } private static class JsonBean{ private String someString; private int someInt; } }

在Configuration类中按照下面的方式使用:

@Configuration @EnableApolloConfig public class AppConfig { @Bean public TestApolloAnnotationBean testApolloAnnotationBean() { return new TestApolloAnnotationBean(); } }

3.2.4 已有配置迁移

不少状况下,应用可能已经有很多配置了,好比Spring Boot的应用,就会有bootstrap.properties/yml, application.properties/yml等配置。

在应用接入Apollo以后,这些配置是能够很是方便的迁移到Apollo的,具体步骤以下:

  1. 在Apollo为应用新建项目
  2. 在应用中配置好META-INF/app.properties
  3. 建议把原先配置先转为properties格式,而后经过Apollo提供的文本编辑模式所有粘帖到应用的application namespace,发布配置
  4. 若是原来是yml,想继续使用yml来编辑配置,那么能够建立私有的application.yml namespace,把原来的配置所有粘贴进去,发布配置
    • 须要apollo-client是1.3.0及以上版本
  5. 把原先的配置文件如bootstrap.properties/yml, application.properties/yml从项目中删除
    • 若是须要保留本地配置文件,须要注意部分配置如server.port必须确保本地文件已经删除该配置项

如:

spring.application.name = reservation-service server.port = 8080 logging.level = ERROR eureka.client.serviceUrl.defaultZone = http://127.0.0.1:8761/eureka/ eureka.client.healthcheck.enabled = true eureka.client.registerWithEureka = true eureka.client.fetchRegistry = true eureka.client.eurekaServiceUrlPollIntervalSeconds = 60 eureka.instance.preferIpAddress = true

text-mode-spring-boot-config-sample

3.3 Demo

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

更多使用案例Demo能够参考Apollo使用场景和示例代码

4、客户端设计

client-architecture

上图简要描述了Apollo客户端的实现原理:

  1. 客户端和服务端保持了一个长链接,从而能第一时间得到配置更新的推送。(经过Http Long Polling实现)
  2. 客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
    • 这是一个fallback机制,为了防止推送机制失效致使配置不更新
    • 客户端定时拉取会上报本地版本,因此通常状况下,对于定时拉取的操做,服务端都会返回304 - Not Modified
    • 定时频率默认为每5分钟拉取一次,客户端也能够经过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟。
  3. 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
  4. 客户端会把从服务端获取到的配置在本地文件系统缓存一份
    • 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
  5. 应用程序能够从Apollo客户端获取最新的配置、订阅配置更新通知

5、本地开发模式

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不会实时监测文件内容是否有变化,因此若是修改了配置,须要重启应用生效。

6、测试模式

1.1.0版本开始增长了apollo-mockserver,从而能够很好地支持单元测试时须要mock配置的场景,使用方法以下:

6.1 引入pom依赖

<dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-mockserver</artifactId> <version>1.1.0</version> </dependency> 

6.2 在test的resources下放置mock的数据

文件名格式约定为mockdata-{namespace}.properties

image

6.3 写测试类

更多使用demo能够参考ApolloMockServerApiTest.javaApolloMockServerSpringIntegrationTest.java

@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = TestConfiguration.class) public class SpringIntegrationTest { // 启动apollo的mockserver @ClassRule public static EmbeddedApollo embeddedApollo = new EmbeddedApollo(); @Test @DirtiesContext // 这个注解颇有必要,由于配置注入会弄脏应用上下文 public void testPropertyInject(){ assertEquals("value1", testBean.key1); assertEquals("value2", testBean.key2); } @Test @DirtiesContext public void testListenerTriggeredByAdd() throws InterruptedException, ExecutionException, TimeoutException { String otherNamespace = "othernamespace"; embeddedApollo.addOrModifyPropery(otherNamespace,"someKey","someValue"); ConfigChangeEvent changeEvent = testBean.futureData.get(5000, TimeUnit.MILLISECONDS); assertEquals(otherNamespace, changeEvent.getNamespace()); assertEquals("someValue", changeEvent.getChange("someKey").getNewValue()); } @EnableApolloConfig("application") @Configuration static class TestConfiguration{ @Bean public TestBean testBean(){ return new TestBean(); } } static class TestBean{ @Value("${key1:default}") String key1; @Value("${key2:default}") String key2; SettableFuture<ConfigChangeEvent> futureData = SettableFuture.create(); @ApolloConfigChangeListener("othernamespace") private void onChange(ConfigChangeEvent changeEvent) { futureData.set(changeEvent); } } }

附:wiki: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
相关文章
相关标签/搜索