携程apollo系列-客户端集成
本文讲解如何在 Java 程序中集成 Apollo 配置, 主要涉及到一些基础用法.
对于一些高级用法, 比如如何加密/解密配置项 (可用于数据库密码配置), 如何动态切换数据源地址,如何动态切换日志级别,
可以参考官方的 user cases git 库, 官方代码库 https://github.com/ctripcorp/apollo-use-cases 和 InfoQ 微信文章: GitHub 9K Star!Apollo作者手把手教你微服务配置中心之道
在开始集成之前, 先了解一些 apollo 相关概念.
================================================
基本概念
================================================
Apollo 几个术语的从属关系: 环境>项目>集群>命名空间.
1. 集群概念
这里的集群不是指 apollo 配置中心集群, 而是指受管项目的集群. 另外需要区分集群和环境术语的差异, 这里的集群相当于数据中心 (idc) 的概念, 比如一个大型系统的生产环境, 可能会部署在多个不同的数据中心.
一个应用可以包含多个集群, 缺省的集群名为"default", 比如在不同机房, 项目使用到 redis 地址不同. 如果不同 idc 使用同样的配置, 则没有必要创建集群. 对于一般项目而言都是部署在同一个数据中心中, 没有必要专门新加集群.
如果在 apollo 中为某个应用配置了集群, 需要在应用的文件系统中, 告知所在集群的名字, 方法是:
在 /opt/settings/server.properties(linux) 文件 或 C:\opt\settings\server.properties(windows) 文件中, 设定 idc 属性值为集群名称.
2. Namespace 概念:
namespace 可以认为是配置项的一个容器, 类似于配置文件的概念.
namespace 的作用:
(1). 作为配置项的一个容器
(2). 用于细化权限管控, 比如某个命名空间只允许 DBA 修改和发布, 另一个只允许运维修改和发布.
(3). 用于配置项版本整体回滚
一个应用可以包含多个 namespace, 缺省的 namespace 名为 "application".
添加 namespace 需要指定配置文件格式, 支持 properties/xml/json/yaml 等格式, 缺省的格式为 properties.
正常情况下一个 namespace 应该是从属于某个应用, 但也可以构建多个应用公用的的 namespace.
对于一个应用而言, namespace 建议分为业务配置和系统配置两类.
(1)业务配置 namespace: 和业务相关的配置.
(2)系统配置 namespace: 包含数据库地址/账号等信息.
3. 环境
apollo 的环境术语和我们平时所讲的环境含义一致, 比如开发/生产环境. apollo 后台的 apolloconfigdb 表仅支持一个环境, 如果要支持多个环境, 必须有多个 MySQL 服务器和多个 config service, 但 apollo portal 是支持多环境集中管理.
apollo 支持 DEV/UAT/PRO/FAT 等环境名称, 大小写不敏感, 还有一个特别的 Local 环境名, 代表程序时仅读取本地缓存目录下的配置文件.
支持的环境名有:
DEV -- dev 环境
FAT -- feature Acceptance test
UAT -- user Acceptance test
PRO -- production 环境
Local -- 读取本地缓存目录下的配置文件
设置 env=Local, 程序仅读本地缓存目录下的配置文件, 下面是缺省的本地缓存路径, 可以通过 apollo.cacheDir 参数来调整缺省的缓存路径
## Mac/Linux: /opt/data/{appId}/config-cache
## Windows: C:\opt\data\{appId}\config-cache
具体的缓存文件名为: {appId}+{cluster}+{namespace}.properties
================================================
pom.xml
================================================
增加 apollo-client 依赖.
<dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.1.0</version> </dependency>
================================================
设定远程 Apollo 服务器连接信息
================================================
Apollo 提供很多方式来设定远程配置服务, 下面仅仅列出其中一些方式.
接入Apollo的 SpringBoot 程序, 推荐使用 System property 配置 apollo 参数, application.properties不要再配加任何参数, 仅仅保留一个空文件, 其他参数都配在 apollo 中, 当然我们不能把apollo服务器连接参数配到apollo中.
另外, 一旦接入了 apollo 系统, 如果 application.properties 和 apollo 中有同名的参数, 以apollo中的配置为准.
方式 1: 通过 System property 形式, 比如命令行增加参数:
System property 支持所有的参数, 黑体字为最重要的参数, 推荐在启动命令行中设定.
-Dapp.id=YOUR-APP-ID
-Dapollo.meta=http://config-service-url
-Denv=DEV
-Dapollo.cacheDir=/opt/data/some-cache-dir
-Dapollo.cluster=SomeCluster
对于 meta server, 也可以 -D{env}.meta 这样的形式指定.
方式 2: 通过 SpringBoot 的 application.properties 文件指定
application.properties 中除了不能指定 env 参数外, 其他都参数都支持.
app.id=YOUR-APP-ID
apollo.meta=http://localhost:8080
apollo.cacheDir=/opt/data/some-cache-dir
apollo.cluster=SomeCluster
apollo.bootstrap.enabled = true
apollo.bootstrap.namespaces = application,other_namespace
apollo.autoUpdateInjectedSpringProperties=true # 自动更新 @Value 注解的属性
方式 3: 通过 META-INF/app.properties 文件设定
只能用来设置 app.id 和 apollo.meta 参数.
app.properties 文件的位置是: classpath:/META-INF/app.properties
app.id=YOUR-APP-ID
apollo.meta=http://localhost:8080
方式 4: 通过 server.properties 配置文件
不能用来设定 app.id 参数, 主要用来设定这台服务器上的通用设置, 比如 meta 和 cluster 和 cacheDir 等参数, 其中 idc 参数等同于 apollo.cluster 参数.
对于 Mac/Linux, 文件位置为/opt/settings/server.properties
对于 Windows, 文件位置为 C:\opt\settings\server.properties
env=DEV
idc=SomeCluster
apollo.meta=http://config-service-url
apollo.cacheDir=/opt/data/some-cache-dir
dev.meta=http://localhost:8080
fat.meta=http://apollo.fat.xxx.com
uat.meta=http://apollo.uat.xxx.com
pro.meta=http://apollo.xxx.com
方式 5: 通过 apollo-env.properties 设置各环境的服务器地址
主要用来指定 meta server 的地址. apollo-env.properties 文件位置是 resources 目录下
dev.meta=http://localhost:8080
fat.meta=http://apollo.fat.xxx.com
uat.meta=http://apollo.uat.xxx.com
pro.meta=http://apollo.xxx.com
================================================
Spring 项目和 apollo 集成方式
================================================
Java项目要集成Apollo 有很多种方式, 下面是常用方式:
方式 1: 使用 Java API 方式, 不仅适合 Spring 项目, 而且适合非 Spring 项目, 灵活方便, 支持热部署.
方式 2: Spring 的 @Value 方式关联 appollo 配置项. 该方式使用简单, 但可控性较差.
方式 3: 结合 SpringBoot 的 @ConfigurationProperties 注解. 该方法使用稍微复杂, 但可控性好.
方式 4. 使用 Apollo 的 @ApolloConfig 和 @ApolloConfigChangeListener .
方式 5. Spring 项目通过 Environment, 该方式使用较为麻烦, 推荐使用方式 2 或 3.
----------------------------------------
方式 1: 使用 Java API 方式
----------------------------------------
// 从缺省的获取 application 命名空间 Config config = ConfigService.getAppConfig(); //获取其他 namespace 的配置 //Config config = ConfigService.getConfig(String namespace, ConfigFileFormat configFileFormat); String someKey = "someKeyFromDefaultNamespace"; String someDefaultValue = "someDefaultValueForTheKey"; String value = config.getProperty(someKey, someDefaultValue); // 增加监听事件 config.addChangeListener(new ConfigChangeListener() { @Override public void onChange(ConfigChangeEvent changeEvent) { 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())); } } });
----------------------------------------
SpringBoot 下预先加载配置项
----------------------------------------
SpringBoot 大量采用了自动装配机制, 比如 DataSource 实例初始化. 当引入 Apollo 配置中心后, 是否也支持这个特性呢? 答案是肯定的, apollo.bootstrap.namespaces 属性指定的 namespace 将在 SpringContext 容器初始化时候就加载完毕, 这样在实例化 bean 时就可能使用 Apollo 配置项了. 需要在 application.properties 中设置下面两个属性:
apollo.bootstrap.enabled = true
apollo.bootstrap.namespaces = application,other_namespace
----------------------------------------
方式 2: Spring 的 @Value 方式关联 appollo 配置项
----------------------------------------
在代码层面该方式仅需要两个步骤:
步骤 1: 在一个被@Configuration 注解的类上加 @EnableApolloConfig, 比如在 Application 入口类上加 @EnableApolloConfig. @EnableApolloConfig 负责将 apollo 的 namespace 注册到 Configuration 类中.
步骤 2: 在程序中使用 @Value 引入这个配置项
@Value("${timeout:1000}") private String timeout;
需要说明的是:
1. @EnableApolloConfig 注解必须和@Configuration 搭配使用, 如果没有指定注解参数, 对应的namespace为 apollo 的 "application" namespace, .
2. 默认情况下, @Value 的属性会自动更新的, 如果不希望自动更新, 可以在 application.properties 设置:
apollo.autoUpdateInjectedSpringProperties=false # 关闭自动更新 @Value 注解的属性
----------------------------------------
方式 3: 结合 SpringBoot 的 @ConfigurationProperties 注解.
----------------------------------------
略
----------------------------------------
方式 4. 使用 Apollo 的 @ApolloConfig 和 @ApolloConfigChangeListener
----------------------------------------
这个方法其实和通过 API 方法非常类似, 仅仅是简化了代码.
使用 @ApolloConfig 用来注入一个 apollo config 对象, 使用 @ApolloConfigChangeListener 用来注册一个监听事件.
@ApolloConfig(namespace) 可以匹配 apollo 的命名空间, 如不写, 则指 application 命名空间.
@ApolloConfigChangeListener(namespace) 更新时调用的方法, 如不写, 则指 application 命名空间.
在代码层面该方式仅需要两个步骤:
1. 在一个被@Configuration 注解的类上加 @EnableApolloConfig, 比如在 Application 入口类上加 @EnableApolloConfig.
2. 增加一个 bean 类,在其中使用 @ApolloConfig 和 @ApolloConfigChangeListener 注入 config 对象和监听事件, 同时加上一个 @PostConstruct 方法来初始化 apollo 配置项原始值.
@EnableApolloConfig @Component class ApolloConfigBean { @ApolloConfig //注入一个 Apollo Config 对象 private Config appConfig; @PostConstruct // 在 ApolloConfigBean 实例化后, 立即获取 apollo 配置项值 public void init() { timeout = appConfig.getIntProperty("timeout", 100); } private int timeout = -1; public int getTimeout() { return timeout; } @ApolloConfigChangeListener //注册一个监听事件 private void onChange(ConfigChangeEvent changeEvent) { if (changeEvent.isChanged("timeout")) { timeout = Integer.parseInt(changeEvent.getChange("timeout") .getNewValue()); } System.out.println("onchange "); } }
----------------------------------------
方式 5. Spring 项目通过 Environment
----------------------------------------
略
================================================
参考
================================================
Apollo 的基本使用方法
http://ghoulich.xninja.org/2018/04/28/basic-usage-method-of-apollo/
Java 客户端使用指南
https://github.com/ctripcorp/apollo/wiki/Java 客户端使用指南
微服务之 SpringCloud 架构第六篇(下)——配置中心(Apollo)
https://blog.csdn.net/pilihaotian/article/details/82958386
Apollo 应用之动态调整线上数据源 (DataSource)
http://www.kailing.pub/article/index/arcid/198.html
SpringBoot 整合携程 Apollo 配置管理中心
https://www.cnblogs.com/hongdada/p/9015748.html
携程 Apollo(阿波罗)配置中心在 Spring Boot 项目快速集成
https://www.cnblogs.com/EasonJim/p/7649047.html
Apollo-4-客户端-SDK-设计
http://thinkinjava.cn/2018/06/Apollo-4-%E5%AE%A2%E6%88%B7%E7%AB%AF-SDK-%E8%AE%BE%E8%AE%A1/