Apollo配置中心使用篇
- 常见配置中心对比
- Apollo核心概念
- Apollo核心特性
- Apollo架构设计
- 各模块介绍
- 服务端设计
- 客户端设计
- Apollo与Spring集成的底层原理
- Apollo安装
- 安装apollo-portal
- config service和admin service部署
- 多网卡问题解决
- 修改Portal环境配置
- 调整ApolloPortal配置
- Apollo权限管理
- 客户端操作
- 集成SpringBoot
- Apollo客户端详细配置
- Environment
- AppId配置方法
- Apollo Meta Server
- 默认本地缓存路径
- 自定义缓存路径
- 配置热更新
- 命名空间
- namespace配置管理
- 关联namespace
- 集群
- 灰度发布
- 放弃灰度与全量发布
- 补充篇
- 客户端监听配置变化
- 获取非properties格式namespace的配置
- 控制Spring中注入的多个namespace的优先级
- 与SpringBoot整合注意事项
- Spring Placeholder的使用
- Spring Annotation支持
- SpringBoot项目已有配置迁移
- 本地开发模式
- 测试模式
本文为Apollo配置中心使用篇学习笔记整理,主要参考资料如下:
- 文档资料:
- Apollo官方文档
- 对Apollo源码感兴趣的可以这个系列: 芋道Apollo源码解析系列(官方文档源码解析链接也是跳转到这里)
如果对配置中心完全没有过了解的,可以先移步去了解一下常用的开源配置中心组件,如: SpringCloud Config和Nacos等。
本文为官方文档的一份精炼,如果查看完整细节,参考各个小节开头给出的官方文档链接
常见配置中心对比
-
Spring Cloud Config: https://github.com/spring-cloud/spring-cloud-config
-
Apollo: https://github.com/ctripcorp/apollo
-
Nacos: https://github.com/alibaba/nacos
对比项目/配置中心 | spring cloud config | apollo | nacos(重点) |
---|---|---|---|
开源时间 | 2014.9 | 2016.5 | 2018.6 |
配置实时推送 | 弱支持(Spring Cloud Bus) | 支持(HTTP长轮询1s内) | 支持(HTTP长轮询1s内) |
版本管理 | 支持(Git) | 自动管理 | 自动管理 |
配置回滚 | 弱支持(Git+Bus) | 支持 | 支持 |
配置的灰度发布 | 理念上支持,可操作性不强 | 支持 | 1.1.0开始支持 |
权限管理 | 不支持(没有区分用户、角色、权限的概念) | 支持 | 1.2.0开始支持 |
多集群多环境 | 对集群概念支持较弱 | 支持 | 支持 |
多语言 | 只支持Java | Go,C++,Python,Java,.net,OpenAPI | Python,Java,Nodejs,OpenAPI |
分布式高可用最小集群数量 | Config-Server2+Git+MQ | Config2+Admin3+Portal*2+Mysql=8 | Nacos*3+MySql=4 |
配置格式校验 | 不支持 | 支持 | 支持 |
通信协议 | HTTP和AMQP | HTTP | HTTP |
数据一致性 | Git保证数据一致性,Config-Server从Git读取数据 | 数据库模拟消息队列,Apollo定时读消息 | HTTP异步通知 |
选择哪一个配置中心呢?
- 如果希望利用Nacos提供的服务注册中心功能,可以考虑Nacos
- 看公司技术选型
Apollo核心概念
本节对应官方文档链接
Apollo(阿波罗)是一款可靠的分布式配置管理中心,诞生于携程框架研发部,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
- 服务端基于Spring Boot和Spring Cloud开发,打包后可以直接运行,不需要额外安装Tomcat等应用容器。
- Java客户端不依赖任何框架,能够运行于所有Java运行时环境,同时对Spring/Spring Boot环境也有较好的支持。
Apollo中有以下几个核心概念:
application (应用)
:当前使用apollo的集中配置管理的应用程序environment (环境)
:生产、测试、开发环境等。或者操作系统的环境:windows、linux等。cluster (集群)
:通常指部署到同一网络机房内的物理主机集群。集群配置信息存在的意义在于:项目部署在不同的集群,使用不同的配置,如:windows和linux服务器集群存在路径差异。如果项目部署在不同的集群,仍然可以使用相同的配置,则没有必要创建集群。namespace (命名空间)
:多个应用之间存在公共应用组件,为公共应用组件配置设置namespace(标签)。方便其他应用引用其配置。
Apollo核心特性
本节对应官方文档链接
- 统一管理不同环境、不同集群的配置
- 配置修改实时生效(热发布)
- 版本发布管理
- 灰度发布
- 权限管理、发布审核、操作审计
- 客户端配置信息监控
- 提供Java和.Net原生客户端
- 提供开放平台API
- 部署简单
Apollo架构设计
本节对应官方文档链接
- 简化的核心交互模型
- 详细架构模型
- Config Service: 我们的微服务实例作为Apollo客户端,借助Config Service完成配置的读取,如果配置有更新Config Service会反向通知客户端
- Admin Service: 我们借助Apollo提供的可视化操作界面即Portal完成配置的修改和发布,实际请求会发送给Admin Service处理
- Config Service和Admin Service为了高可用考虑,需要多实例部署,因此需要将自己注册到注册中心Eureka中并保持心跳,这两个服务本身也是Spring Boot服务,同样需要服务注册与发现。
- 在Eureka上面,我们架设一层Meta Server用于封装Eureka的服务发现接口(屏蔽底层具体注册中心组件,方便日后动态替换)
- Client通过域名访问Meta Server获取Config Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Client侧会做load balance、错误重试
- Portal通过域名访问Meta Server获取Admin Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Portal侧会做load balance、错误重试
- 为了简化部署,我们实际上会把Config Service、Eureka和Meta Server三个逻辑角色部署在同一个JVM进程中
为什么注册中心选择Eureka,而不是zk或者etcd呢?
- 它提供了完整的Service Registry和Service Discovery实现
- 和Spring Cloud无缝集成
- 我们的项目本身就使用了Spring Cloud和Spring Boot,同时Spring Cloud还有一套非常完善的开源代码来整合Eureka,所以使用起来非常方便。
- 另外,Eureka还支持在我们应用自身的容器中启动,也就是说我们的应用启动完之后,既充当了Eureka的角色,同时也是服务的提供者。这样就极大的提高了服务的可用性。
- 这一点是我们选择Eureka而不是zk、etcd等的主要原因,为了提高配置中心的可用性和降低部署复杂度,我们需要尽可能地减少外部依赖。
- 最后一点是开源,由于代码是开源的,所以非常便于我们了解它的实现原理和排查问题。
各模块介绍
本节对应官方文档链接
-
Config Service
- 提供配置获取接口
- 提供配置更新推送接口
(基于Http long polling)
- 服务端使用Spring DeferredResult实现异步化,从而大大增加长连接数量
- 目前使用的tomcat embed默认配置是最多10000个连接(可以调整),使用了4C8G的虚拟机实测可以支撑10000个连接,所以满足需求(一个应用实例只会发起一个长连接)。
- 接口服务对象为Apollo客户端
-
Admin Service
- 提供配置管理接口
- 提供配置修改、发布等接口
- 接口服务对象为Portal
-
Meta Server
- Portal通过域名访问Meta Server获取Admin Service服务列表(IP+Port)
- Client通过域名访问Meta Server获取Config Service服务列表(IP+Port)
- Meta Server从Eureka获取Config Service和Admin Service的服务信息,相当于是一个Eureka Client
- 增设一个Meta Server的角色主要是为了封装服务发现的细节,对Portal和Client而言,永远通过一个Http接口获取Admin - Service和Config Service的服务信息,而不需要关心背后实际的服务注册和发现组件
- Meta Server只是一个逻辑角色,在部署时和Config Service是在一个JVM进程中的,所以IP、端口和Config Service一致
-
Eureka
- 基于Eureka和Spring Cloud Netflix提供服务注册和发现
- Config Service和Admin Service会向Eureka注册服务,并保持心跳
- 为了简单起见,目前Eureka在部署时和Config Service是在一个JVM进程中的(通过Spring Cloud Netflix)
-
Portal
- 提供Web界面供用户管理配置
- 通过Meta Server获取Admin Service服务列表(IP+Port),通过IP+Port访问服务
- 在Portal侧做load balance、错误重试
-
Client
- Apollo提供的客户端程序,为应用提供配置获取、实时更新等功能
- 通过Meta Server获取Config Service服务列表(IP+Port),通过IP+Port访问服务
- 在Client侧做load balance、错误重试
服务端设计
本节对应官方文档链接
在配置中心中,一个重要的功能就是配置发布后实时推送到客户端。下面我们简要看一下这块是怎么设计实现的。
- 用户在Portal操作配置发布
- Portal调用Admin Service的接口操作发布
- Admin Service发布配置后,发送ReleaseMessage给各个Config Service
- Config Service收到ReleaseMessage后,通知对应的客户端
- 发送ReleaseMessage的实现方式
用户通过Portal提供的界面发布配置后,配置发布请求最终交给Admin Service处理,Admin Service在配置发布后,需要通知所有的Config Service有配置发布,从而Config Service可以通知对应的客户端来拉取最新的配置。
从概念上来看,这是一个典型的消息使用场景,Admin Service作为producer发出消息,各个Config Service作为consumer消费消息。通过一个消息组件(Message Queue)就能很好的实现Admin Service和Config Service的解耦。
在实现上,考虑到Apollo的实际使用场景,以及为了尽可能减少外部依赖,我们没有采用外部的消息中间件,而是通过数据库实现了一个简单的消息队列。
实现方式如下:
- Admin Service在配置发布后会往ReleaseMessage表插入一条消息记录,消息内容就是配置发布的
AppId+Cluster+Namespace
- Config Service有一个线程会每秒扫描一次ReleaseMessage表,看看是否有新的消息记录
- Config Service如果发现有新的消息记录,那么就会通知到所有的消息监听器(ReleaseMessageListener),如NotificationControllerV2
- NotificationControllerV2得到配置发布的
AppId+Cluster+Namespace
后,会通知对应的客户端
- Config Service通知客户端的实现方式
上一节中简要描述了NotificationControllerV2是如何得知有配置发布的,那NotificationControllerV2在得知有配置发布后是如何通知到客户端的呢?
实现方式如下:
- 客户端会发起一个Http请求到Config Service的notifications/v2接口,也就是NotificationControllerV2
- NotificationControllerV2不会立即返回结果,而是通过Spring DeferredResult把请求挂起
- 如果在60秒内没有该客户端关心的配置发布,那么会返回Http状态码304给客户端
- 如果有该客户端关心的配置发布,NotificationControllerV2会调用DeferredResult的setResult方法,传入有配置变化的namespace信息,同时该请求会立即返回。客户端从返回的结果中获取到配置变化的namespace后,会立即请求Config Service获取该namespace的最新配置。
上面过程涉及源码可以翻阅:RemoteConfigLongPollService ,客户端建立与服务端长链接源码简单来说如下所示:
客户端设计
本节对应官方文档链接
- 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现)
- 客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
- 这是一个fallback机制,为了防止推送机制失效导致配置不更新
- 客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
- 定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟。
- 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
- 客户端会把从服务端获取到的配置在本地文件系统缓存一份
- 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
- 应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知
Apollo与Spring集成的底层原理
本节对应官方文档链接
Apollo除了支持API方式获取配置,也支持和Spring/Spring Boot集成,集成原理简述如下。
Spring从3.1版本开始增加了ConfigurableEnvironment和PropertySource
:
- ConfigurableEnvironment
- Spring的ApplicationContext会包含一个Environment(实现ConfigurableEnvironment接口)
- ConfigurableEnvironment自身包含了很多个PropertySource
- PropertySource
- 属性源
- 可以理解为很多个Key - Value的属性配置
在运行时的结构形如:
需要注意的是,PropertySource之间是有优先级顺序的,如果有一个Key在多个property source中都存在,那么在前面的property source优先。
所以对上图的例子:
- env.getProperty(“key1”) -> value1
- env.getProperty(“key2”) -> value2
- env.getProperty(“key3”) -> value4
在理解了上述原理后,Apollo和Spring/Spring Boot集成的手段就呼之欲出了:在应用启动阶段,Apollo从远端获取配置,然后组装成PropertySource并插入到第一个即可,如下图所示:
相关代码可以参考: PropertySourcesProcessor
- PropertySourcesProcessor实现了BeanFactoryPostProcessor,意味着BeanFactoryPostProcessor可以在BeanFactory初始化并且prepare后,对BeanFactory进行一些后置处理,核心方法逻辑体现在postProcessBeanFactory中
- PropertySourcesProcessor还继承了EnvironmentAware接口,所以可以获取当前当前ApplicationContext应用程序上下文中的环境对象Environment
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
this.configUtil = ApolloInjector.getInstance(ConfigUtil.class);
//将ApolloPropertySource添加进Environment对象中,这样@Value或者@ConfigurationProperties进行属性注入时
//就可以从Environment环境上下文对象的PropertySource集合中发现ApolloPropertySource
//并从ApolloPropertySource中根据Key提取中我们想要的结果
initializePropertySources();
initializeAutoUpdatePropertiesFeature(beanFactory);
}
//注意: 以下方法极度简化,并进行了改动
private void initializePropertySources() {
CompositePropertySource composite=new CompositePropertySource(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME);
//遍历所有命空间
for (String namespace : NAMESPACE_NAMES) {
//拿到命名空间下的配置
Config config = ConfigService.getConfig(namespace);
//组装成一个PropertySource加入CompositePropertySource中
composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
}
//作为first优先级添加进environment中
environment.getPropertySources().addFirst(composite);
}
此部分涉及Spring Environment环境上下文模块的前置知识,如果不了解的小伙伴,可以先去了解一下环境上下文模块的架构设计和相关组件类及源码。
Apollo安装
本节对应官方文档链接,本文未使用脚本安装,而是一步步安装,方便大家理解原理,如果需要使用脚本快速安装,可以参考给出的官方文档链接
- 环境要求:
Java版本最低要求
- Apollo服务端:1.8+
- Apollo客户端:1.7+
MySQL版本要求:5.6.5+
- Apollo的表结构对timestamp使用了多个default声明,所以需要5.6.5以上版本。
- mysql版本检查命令为: SHOW VARIABLES WHERE Variable_name = 'version';
- 下载apollo安装包: https://github.com/ctripcorp/apollo/releases
注意:
- portal是apollo配置管理的WEB界面,一般部署在生产环境主机上
- config-service和admin-service是区分环境对配置进行管理的,因此通常生产环境部署一套,测试环境部署一套,研发环境部署一套
- Apollo 服务端总共需要两个数据库:ApolloPortalDB和 ApolloConfigDB ,下载后导入数据库即可
- 两个数据库的sql文件下载地址: https://github.com/apolloconfig/apollo/tree/master/scripts/sql
# 用管理员账号登录mysql之后执行命令,创建数据库、建表及插入示例数据。
mysql > source 你的SQL所在的linux文件路径/apolloportaldb.sql
# 建立test用户访问ApolloPortalDB和ApolloConfigDB
CREATE USER 'test'@'%' IDENTIFIED BY '你的密码';
GRANT ALL ON ApolloPortalDB.* TO 'test'@'%' IDENTIFIED BY '你的密码'; //只有执行了这一句才可以远程登陆
GRANT ALL ON ApolloConfigDB.* TO 'test'@'%' IDENTIFIED BY '你的密码';
安装apollo-portal
- 解压apollo-portal.zip
#在当前目录下创建portal 目录,并将文件解压到里面
mkdir ./portal && unzip apollo-portal-2.1.0-github.zip -d ./portal
- 数据库配置修改
- apollo-portal-1.6.1-github.zip包解压出来有一个config/application-github.properties文件,修改其中的mysql ip地址、端口、用户名、密码信息,指向ApolloPortalDB所在的mysql数据库。
- 根据你的数据库ApolloPortalDB地址将localhost修改为数据库主机ip
- 根据你的ApolloPortalDB实际数据库修改用户名密码
- apollo-portal-1.6.1-github.zip包解压出来有一个config/application-github.properties文件,修改其中的mysql ip地址、端口、用户名、密码信息,指向ApolloPortalDB所在的mysql数据库。
# DataSource
spring.datasource.url = jdbc:mysql://119.91.143.140:3306/ApolloPortalDB?characterEncoding=utf8
spring.datasource.username = test
spring.datasource.password = xxx
- 多环境配置文件
- Apollo Portal支持多环境配置,其配置文件是config/apollo-env.properties。Apollo Portal需要根据环境的不同,访问不同的meta service(apollo-configservice)地址,所以需要在配置中提供ConfigService服务地址信息。
local.meta=http://localhost:8080
dev.meta=http://fill-in-dev-meta-server:8080
fat.meta=http://fill-in-fat-meta-server:8080
uat.meta=http://fill-in-uat-meta-server:8080
lpt.meta=${lpt_meta}
pro.meta=http://fill-in-pro-meta-server:8080
因为我们还没部署任何生产、测试、或者其他环境,所以暂时将文件内容删掉即可(上面的配置是文件内的默认配置,删掉)。
apollo默认支持的环境单词缩写解释:
- LOCAL:本地开发环境
- DEV:开发环境
- FWS:功能Web服务测试环境
-FAT:功能验收测试环境 - UAT:用户接受度测试环境
- LPT:负载和性能测试环境
- PRO:生产环境
- TOOLS:工具环境,生产环境中的一个特殊区域,它允许访问测试环境,例如Apollo Portal应该部署在工具环境中。
启动Apollo配置中心:
- 修改端口: apollo portal的默认端口是8070,不好,一方面容易发生冲突,一方面容易被安全攻击。所以我们改一下端口,改成不常用的即可。
- 修改scripts/startup.sh脚本(注意该启动脚本里面还指定了日志文件的位置:LOG_DIR)
-
执行启动脚本: ./startup.sh
- 如果你去LOG_DIR目录下查看日志,会发现日志中有一些connect timed out(连接超时的异常),这是因为ApolloPortalDB中有一些默认的DEV环境配置,而我们还没有搭建任何的环境,只是搭建了一个统一管理中心的界面应用portal。所以connect timed out(连接超时的异常)可以暂时忽略。
-
访问测试: http://apollo-portal主机ip:9430/
输入用户名、密码 apollo/admin,登陆成功就表示我们安装成功了。
config service和admin service部署
上一节我们成功安装了portal, portal是用来管理多个运行环境,比如:生产环境、测试环境、开发环境等。
本节我们就以DEV(开发环境)来为大家介绍apollo服务端其他组件的部署,也就是config-service和adminservice。
为了保证高可用,config service和admin service通常都会采用多实例集群部署
-
数据库导入: ConfigService 和 AdminServie都需要操作数据库ApolloConfigDB,sql文件下载链接上面已经给出
-
解压相关压缩文件
mkdir ./configservice && unzip apollo-configservice-2.1.0-github.zip -d ./configservice
mkdir ./adminservice && unzip apollo-configservice-2.1.0-github.zip -d ./adminservice
- 分别修改两个服务的数据库相关配置
spring.datasource.url = jdbc:mysql://你的mysql所在主机ip:3306/ApolloConfigDB?characterEncoding=utf8
spring.datasource.username = test
spring.datasource.password = 你的用户密码
如果是集群部署可以参考下文对ConfigService和AdminService的配置修改,如果采用单机部署,可以直接跳到脚本启动部分,默认配置可以不改
- 修改Eureka服务注册中心连接配置
- ConfigService和AdminService是Spring Cloud微服务,所以二者需要向服务注册中心注册
- ConfigService包含eureka,所以实际上就是向ConfigService注册。
- ConfigService和AdminService都操作ApolloConfigDB,为了避免重复配置,所以官方将eureka连接配置放在了数据库里面。
假设ConfigService和AdminService都采用集群部署,并且部署在三个节点上,ConfigService端口号为9431,AdminService端口号为9432
# 在ApolloConfigDB中执行如下的SQL,配置eureka集群连接。
UPDATE apolloconfigdb.ServerConfig
SET ServerConfig.`Value`='http://192.168.161.3:9431/eureka/,http://192.168.161.4:9431/eureka/,http://192.168.161.5:9431/eureka/'
WHERE `Key`='eureka.service.url';
-
ConfigService和AdminService服务启动
- 先改端口: ConfigService和AdminService的默认端口是8080, 8090端口,按照我们的规划改成9431和9432。修改scripts/startup.sh脚本(三台服务器182.168.161.3,192.168.161.4,192.168.161.5都要改,所以可以将zip里面文件中端口改好之后再上传服务器)。
-
执行启动脚本
- 分别进入三台服务器182.168.161.3,192.168.161.4,192.168.161.5的configservice、adminservice目录下。脚本的启动顺序没有必然的先后(连接失败可以重试),但最好先启动configservice。执行startup.sh脚本(脚本的路径根据你自己的主机目录修改):
/root/apollo/configservice/scripts/startup.sh; #先在每台机器上执行这个,因为包含eureka。
/root/apollo/adminservice/scripts/startup.sh; #然后每台机器上执行这个
- 部署结果验证
- 验证eureka访问: http://192.168.161.3:9431/ ,http://192.168.161.4:9431/ ,http://192.168.161.5:9431/ ,然后去检查所有实例是否启动成功。
- 验证eureka访问: http://192.168.161.3:9431/ ,http://192.168.161.4:9431/ ,http://192.168.161.5:9431/ ,然后去检查所有实例是否启动成功。
多网卡问题解决
eureka是spring cloud服务,所以该解决方案适用于所有的spring cloud服务。也就适用于configService和adminservice。在二者的application-github.properties文件中加入网卡选择配置。
spring.cloud.inetutils.preferredNetworks=192.168
spring.cloud.inetutils.ignoredInterfaces[0]=enp0s3
spring.cloud.inetutils.ignoredInterfaces[1]=docker0
eureka.instance.hostname=peer1
eureka.instance.instance-id: ${spring.application.name}-${eureka.instance.hostname}:${server.port}
将hostname改为peer1、peer2、peer3分别应用于三台主机,配置修改完成之后,重启configservice、adminservice
configService中默认包含了eureka,所以所有eureka遇到的问题都可以在configService应用中通过配置解决。
修改Portal环境配置
因为我们新增了DEV开发环境,并且部署了ConfigService和AdminService,所以需要将其告知Apollo Portal。方法就是修改portal应用的配置文件是config/apollo-env.properties。
dev.meta=http://192.168.161.3:9431,http://192.168.161.4:9431,http://192.168.161.5:9431
将dev.meta的配置指向configService服务。Config service地址也可以填入IP,0.11.0版本之前只支持填入一个IP。从0.11.0版本开始支持填入以逗号分隔的多个地址。如上配置所示。
不过对于生产环境还是建议使用域名,通过SLB(Software Load Balancer:nginx or haproxy)做动态负载均衡,因为机器扩容、缩容等都可能导致IP列表的变化。
因为我们修改了portal配置文件,所以portal也要重启。
/root/apollo/portal/scripts/startup.sh;
调整ApolloPortal配置
服务配置项统一存储在ApolloPortalDB.ServerConfig表中,可以通过管理员工具 - 系统参数
页面进行配置:
- apollo.portal.envs - 可支持的环境列表
默认值是dev(开发环境),不用改。如果我们本节内容新增的是生产环境的配置,应该写上“dev,pro”,然后保存。查看管理员工具->系统信息,如下:
如果采用集群化部署,那么下面展示的就是多个实例,如第二张图所示
Apollo权限管理
本节对应官方文档链接
- apollo的权限管理设计,采用的是典型的RABC权限管理模型
- 用户与角色是多对多关系
- 角色与权限是多对多关系
Apollo默认定义了如下的一些权限:
- CreateCluster 创建集群
- CreateNamespace 创建namespace
- ModifyNamespace 修改namespqce
- AssignRole 分配角色
- ReleaseNamespace 为某项目删掉namespace标签
- CreateApplication 创建项目
- Apollo项目配置管理
- 一个App可部署到多个集群Cluster
- 一个集群Cluster可以有多个命名空间namespace(可以理解为命名空间就是标签)
- 一个App可以打上多个namespace标签(关系存储在appnamespace表里面)
- 部门管理
- 上面两张图中没有体现App与部门之间的关系:一个APP只能归属于一个部门,app项目表中包含该项目所属的部门。通过:管理员工具->系统参数,可以在apollo中配置部门信息。参数key:organizations,先查询,在修改、保存。
Apollo的系统参数管理功能,就是给专业的IT运维人员用的,需要掌握JSON才能修改数据。并不针对小白用户提升用户体验。比如:我将样例部门二改为研发部,JSON代码如下:
[{"orgId":"TEST1","orgName":"样例部门1"},{"orgId":"yanfa","orgName":"研发部"}]
- 用户管理
- 通过:管理员工具->用户管理。下面界面填写信息,用户存在则更新密码或邮箱,不存在则新增用户。
除非我们设置了系统参数role.create-application.enabled=true
,否则新建的用户默认就具有创建项目的permission了。如果设置了role.create-application.enabled=true
,需要通过:管理员工具->系统权限管理,为新建用户增加创建项目的权限。
官方文档参数说明链接
没有必要设置role.create-application.enabled=true,默认就可以,我们也没有必要进行下图中的设置。
- 项目管理功能
- 我们在上面的用户创建过程中,创建了两个用户:dhy和xpy。假设dhy是项目负责人,xpy是项目的配置管理员
- 创建项目
- 使用dhy登录apollo,点击“创建项目”按钮(非常显著的位置)。输入项目信息:
- 部门:选择应用所在的部门(上文讲过如何创建)
- 应用AppId:用来标识应用身份的唯一id,格式为string,需要和项目配置文件applications.properties中配置的app.id对应
- 应用名称:应用名,仅用于界面展示
- 应用负责人:选择的人默认会成为该项目的管理员,具备项目权限管理、集群创建、Namespace创建等权限。
- 使用dhy登录apollo,点击“创建项目”按钮(非常显著的位置)。输入项目信息:
- 为项目组其他的用户赋权
创建项目成功后,会自动跳转到项目首页。
dhy作为项目负责人不可能所有的事都自己做,他安排配置管理员xpy负责项目的配置管理。那么就需要给dhy授权。点击右上角的“授权”按钮:
- 可以分别授予修改权限和发布权限
- 可以针对环境(DEV、PRO等)授权
授权完成之后,zimud登录apollo之后就可以管理我们新建的项目apollo-javaclient-test了。
- 删除项目
- 删除项目:项目负责人dhy和项目配置管理员xpy都没有这个权限。得去找公司的配置管理员apollo。
- 点击右上角的“管理员工具–》删除应用、集群…”,首先查询出要删除的项目,点击“删除应用”
客户端操作
本节对应的官方文档链接
本节介绍一下普通的java项目如何集成apollo-java客户端进行集中配置管理。
- 配置发布: 我们在上一节在apollo新建的项目javaclient-test的基础上,新增一个配置项:test.enabled
点击“提交”按钮保存配置项目。然后点击绿色的“发布”按钮,将配置项信息发布。
- 新建apollo-client工程,引入apollo-client客户端依赖
注意:apollo-client最低要求的JDK版本为1.7,建议使用1.8+
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.7.0</version>
</dependency>
- 编写测试用例
public class ApolloClientTest {
private static final String TEST_KEY="test.enabled";
@Test
public void testSimpleUse(){
Config config = ConfigService.getAppConfig();
//要获取的属性key,如果key不存在,使用我们提供的默认值
String val = config.getProperty(TEST_KEY, null);
System.out.println(TEST_KEY+": "+val);
}
}
- 配置VM options,设置系统属性
-Dapp.id=javaclient-test -Denv=DEV -Ddev_meta=http://8.134.144.48:8080
- app.id必须和apollo配置中心配置的AppID一致:javaclient-test
- env也必须是javaclient-test,存在的环境(DEV开发环境我们部署过)
- dev_meta用来指定DEV环境的ConfigService服务地址(端口8080是ConfigService默认监听端口)
如果获取配置失败,检查是否是因为config service和admin service在eureka注册中心注册的是内网IP,查看客户端日志输出即可,如果看到有私有IP地址出现,说明存在该问题,参考多网卡问题解决一节,或者我们可以手动指定注册的ip,如下所示:
eureka:
instance:
prefer-ip-address: true # 使用IP地址进行注册
ip-address: 192.168.1.100 # 指定要使用的IP地址
运行ApolloConfigTest,观察输出结果:“test.enabled: true”,表示我们从apollo配置中心获取配置成功了。
集成SpringBoot
本节对应官方文档链接
- 引入依赖
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.7.0</version>
</dependency>
- 在resources目录下新建apollo-env.properties文件
- 为每一种环境指定获取配置的configservice地址,configservice地址可以制定多个,用逗号分隔
dev.meta=http://192.168.161.3:9431,http://192.168.161.4:9431,http://192.168.161.5:9431
#pro.meta=http://localhost:8081 //我们暂时没有在apollo中心添加生产环境,注释掉
- 在application.yml中配置appid和metaservice(configservice)服务地址
app:
id: javaclient-test
apollo:
bootstrap:
enabled: true
meta: http://192.168.161.3:9431,http://192.168.161.4:9431,http://192.168.161.5:9431
- appid要与apollo服务配置中心新建项目时候,配置的appid一致
- apollo.meta作用是:当apollo客户端从apollo-env.properties配置的configservice无法取得连接时,会找apollo.meta配置信息去获取连接。所以它是起到一个备份的作用(我们目前只搭建了DEV环境,就和dev.meta配置一样就可以)
- apollo.bootstrap.enabled=true表示在应用一启动的时候,就去加载配置信息。这对于SpringBoot中的一些配置装载类的正确执行来说很重要
- 启用apollo配置开关: 在应用的启动类添加@EnableApolloConfig注解即可:
@SpringBootApplication
@EnableApolloConfig
public class ApolloClientDemoMain {
public static void main(String[] args) {
SpringApplication.run(ApolloClientDemoMain.class,args);
}
}
- 配置发布: 参考上面小节
apollo配置中心目前只支持properties格式,如果需要将使用yaml管理的配置放置到apollo上,我们需要转换一下:可以使用这个网址在线转换: ToYaml.com
- apollo的配置发布之后,我们就可以启动应用了,在启动之前我们需要去配置启动参数:
-Denv=DEV
,也就是告诉springboot应用去加载DEV环境的配置信息。 - 在linux环境下启动,使用如下命令:
java -jar test.jar -Denv=DEV
- 成功标识: apollo的项目视图中,“实例列表”显示我们启动的实例,已经正确的将配置信息获取到
- 启动日志不报错,明确显示如下信息
集成了apollo客户端之后,以后所有的需要写入application.yml配置文件的配置信息,就不要再写到application.yml配置文件里面了,而是去apollo服务配置中心进行增加或者修改。
Apollo客户端详细配置
本节对应官方文档链接
Apollo客户端依赖于AppId,Apollo Meta Server等环境信息来工作,所以请确保下面的配置正确。每种配置都有好几种实现方式,我们在实际的开发中,只使用其中一种就可以,我会在我选择的那种上加上“推荐”二字:
Environment
Environment可以通过以下3种方式的任意一个配置:
- 通过Java System Property(推荐)
- 在Java程序启动脚本中,可以指定-Denv=YOUR-ENVIRONMENT
- 如果是运行jar文件,需要注意格式是java -Denv=YOUR-ENVIRONMENT -jar xxx.jar
注意key为全小写
- 通过操作系统的System Environment
- 还可以通过操作系统的System Environment 的ENV环境变量来指定,注意key为全大写
- 通过配置文件
- 对于Mac/Linux,文件位置为/opt/settings/server.properties
- 对于Windows,文件位置为C:\opt\settings\server.properties
- 文件内容形如:env=DEV
目前,env支持以下几个值(大小写不敏感):
- DEV
- FAT
- UAT
- PRO
AppId配置方法
AppId是应用的身份信息,是配置中心的一个项目id,一般和应用名称保持一致,是从服务端获取配置的一个重要信息。有以下3种方式设置,按照优先级从高到底分别为:
- System Property
- 通过System Property传入app.id信息,如:
-Dapp.id=YOUR-APP-ID
- 通过System Property传入app.id信息,如:
- Spring Boot application.properties(.yml)(推荐)
- 通过Spring Boot的application.properties文件配置,如:
app.id=YOUR-APP-ID
- 通过Spring Boot的application.properties文件配置,如:
- app.properties
- 确保classpath:/META-INF/app.properties文件存在,并且其中内容形如:
app.id=YOUR-APP-ID
- 确保classpath:/META-INF/app.properties文件存在,并且其中内容形如:
Apollo Meta Server
Apollo支持应用在不同的环境有不同的配置,所以需要在运行提供给Apollo客户端当前环境信息。默认情况下,meta server和config service是部署在同一个JVM进程,所以meta server的地址就是config service的地址。支持以下方式配置apollo meta server信息,按照优先级从高到底分别为:
- 可以通过Java的System Property apollo.meta来指定
- 启动脚本中,格式为: -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”);
- 通过Spring Boot的配置文件(推荐)
- 可以在Spring Boot的application.properties或bootstrap.properties中指定apollo.meta=http://config-service-url
- 通过操作系统的System Environment APOLLO_META
- 可以通过操作系统的System Environment APOLLO_META来指定,key为全大写,且中间是_分隔
- 通过server.properties配置文件
- 可以在server.properties配置文件中指定apollo.meta=http://config-service-url
- 对于Mac/Linux,文件位置为/opt/settings/server.properties
- 对于Windows,文件位置为C:\opt\settings\server.properties
- 通过app.properties配置文件
- 可以在classpath:/META-INF/app.properties指定apollo.meta=http://config-service-url
- 通过Java system property ${env}_meta
- 如果当前env是dev,那么用户可以配置-Ddev_meta=http://config-service-url
- 通过apollo-env.properties文件(推荐)
- 这种方法可以根据环境的不同,去不同的configservice获取配置信息,从而达到区分环境加载配置信息的作用,所以推荐
- 用户也可以创建一个apollo-env.properties,放在程序的classpath下,或者放在spring boot应用的config目录下,文件内容形如:
- 如果通过下面的各种环境metaservice都无法获取到Meta Server地址,Apollo最终会fallback到http://apollo.meta作为Meta Server地址
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
默认本地缓存路径
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
- 文件内容以properties格式存储,key:value。
自定义缓存路径
Apollo 1.0.0版本开始支持以下方式自定义缓存路径,按照优先级从高到底分别为:
- 通过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”);
- 通过Spring Boot的配置文件
- 可以在Spring Boot的application.properties或bootstrap.properties中指定apollo.cacheDir=/opt/data/some-cache-dir
- 通过操作系统的System Environment APOLLO_CACHEDIR
- 可以通过操作系统的System Environment APOLLO_CACHEDIR来指定
注意key为全大写,且中间是_分隔
- 可以通过操作系统的System Environment APOLLO_CACHEDIR来指定
- 通过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都挂掉的情况下需要扩容,那么也可以先把配置从已有机器上的缓存路径复制到新机器上的相同缓存路径。
配置热更新
配置主要可以分为两类:
- 第一类是影响应用运行状态的配置,这一类的配置通常会影响Spring Bean的自动装载。
- 比如:数据库连接配置,在应用启动的时候会自动根据数据库配置初始化一个数据库连接池,连接池中保存着n个激活的数据库连接,以供业务持久化操作调用。
- 这一类的配置是不能热更新的,或者准确的说即使配置数据本身更新了也没有用,数据库用户名密码配置更新了不等于数据库连接池里面的连接对象也更新了。
- 配置背后的应用对象重构工作,apollo是无法帮你做到的(配置更新后只有应用重启才能生效)
第一类配置的热更新也不是完全无法做到,可以自己写程序对配置数据变化进行监听,然后重新初始化其关联对象就可以实现。
- 第二类是业务运行所需的数据,比如:新建用户时的默认密码,重置用户时的默认密码。这一类的配置发生变更修改的就是配置数据本身,它不去影响程序的其他对象,不产生其他的连锁反应。
如何实现配置热更新:
- Spring Boot中最常用的配置注解,Apollo都可以实现配置自动更新。
@RefreshScope //这里需要加上RefreshScope注解,结合ConfigurationProperties一起使用
@ConfigurationProperties(prefix = "user.init")
public class User{
private String password;
}
或者
public class Xxxxx{
@Value("${user.init.password}")
private String defaultPwd;
}
需要注意的是,@ConfigurationProperties
如果需要在Apollo配置变化时自动更新注入的值,需要配合使用EnvironmentChangeEvent或RefreshScope。相关代码实现,可以参考apollo-use-cases项目中的ZuulPropertiesRefresher.java和apollo-demo项目中的SampleRedisConfig.java以及SpringBootApolloRefreshConfig.java
命名空间
本节对应官方文档链接
Apollo中的namespace理解为一个单独抽取出来的配置文件,利用namespace我们可以很方便的解决公共配置抽取与复用的问题。
- 所有的spring boot应用默认有一个namespace,就是application(类比: Spring Boot项目都有一个默认配置文件application.yml)
- namespace配置根据获取权限分为两种
- private 私有的namespace,私有的namespace只能被创建该namespace的apollo客户端项目关联使用。
- public 共有的namespace,可以被所有的apollo客户端应用或服务关联使用。
- 一个项目的配置有两部分组成:自有配置和namespace公有配置
- namespace的名称必须全局唯一
- 如果一个项目的自有配置和namespace公有配置的配置项发生冲突,以自有配置的配置项为准,可以覆盖namespace公有配置。
namespace配置管理
- namespace的创建
为namespace名称自动添加部分前缀有利于保障namespace的全局唯一性。在那么space创建完成之后,我们发现项目的配置管理界面分为上下两部分:
- 上半部分是自有配置
- 下半部分是namespace共有配置
- Spring Boot应用程序使用namespace公共配置
- 在spring boot的配置项中,加入apollo.bootstrap.namespaces配置项即可。
app:
id: javaclient-test
apollo:
bootstrap:
enabled: true
namespace: application,TEST1.test-namespace
meta: http://xxx:8080
server:
port: 9200
- 或者通过注解指明使用到的namespace
@EnableApolloConfig({"application","TEST1.test-namespace"})
关联namespace
- 新建一个test2项目,关联公共namespace,达到复用配置的目的
- 关联公共namespace
- 查看关联后的效果
- 在spring boot的配置项中,加入apollo.bootstrap.namespaces配置项
- 然后启动应用程序,测试应用程序就可以正确加载namespace:TEST1.test-namespace的配置项。
集群
集群的概念要比namespace简单的多,“集群”就起到一个为应用按部署集群(机房)不同进行配置管理分类的作用
。某些大型企业,为了满足异地容灾的需要,通常将应用部署在不同的机房。项目A在B机房有一套配置(IP等环境不同),在C机房有一套配置。为了能区分配置的不同,apollo衍生出了集群的概念。
所以:apollo配置中心要为项目增加集群,一定是项目在不同集群(机房)部署的时候配置不同。如果应用在不同的集群(机房)可以使用相同的配置,就没必要为项目添加集群
。
- 点击添加集群
- 集群创建完成后,发现我们的DEV环境下出现了两个集群,一个是default集群,一个是我们新建的TEST集群。
- 上图显示:项目在DEV环境下,TEST集群暂时没有任何配置项。(因为集群是新建的)。我们之前配置的配置项信息,都属于default集群。
- Springboot项目如何使用集群配置?
apollo:
cluster: TEST
java -jar -Dapollo.cluster=TEST
灰度发布
灰度发布就是让配置先在部分实例生效,如果效果理想全量发布到所有实例,如果效果不理想就可以放弃当前的“灰度发布配置”。
Apollo与灰度发布:
- 对于一些对程序有比较大影响的配置,可以先在一个或者多个实例生效,观察一段时间没问题后再全量发布配置。
- 对于一些需要调优的配置参数,可以通过灰度发布功能来实现A/B测试。可以在不同的机器上应用不同的配置,不断调整、测评一段时间后找出较优的配置再全量发布配置。
- 项目自有配置和namespace公有配置都可以实现灰度发布,但是二者是分开实现的
- 项目自有配置在所属项目配置管理界面操作,namespace公有配置在“创建namesapce”的项目管理进行操作
这里以日志输出级别的动态调整为例,来演示一下灰度发布的使用:
- 默认情况下,Spring Boot日志输出级别在apollo中是无法实现热更新的,但是我们可以自己实现。
- 实现的代码如下:
@Service
public class LoggerConfiguration {
private static final Logger logger = LoggerFactory.getLogger(LoggerConfiguration.class);
private static final String LOGGER_TAG = "logging.level.";
@Resource
private LoggingSystem loggingSystem;
@ApolloConfig
private Config config;
@ApolloConfigChangeListener
private void configChangeListter(ConfigChangeEvent changeEvent) {
refreshLoggingLevels();
}
@PostConstruct
private void refreshLoggingLevels() {
Set<String> keyNames = config.getPropertyNames();
for (String key : keyNames) {
if (containsIgnoreCase(key, LOGGER_TAG)) {
String strLevel = config.getProperty(key, "info");
LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());
loggingSystem.setLogLevel(key.replace(LOGGER_TAG, ""), level);
logger.info("{}:{}", key, strLevel);
}
}
}
private static boolean containsIgnoreCase(String str, String searchStr) {
if (str == null || searchStr == null) {
return false;
}
int len = searchStr.length();
int max = str.length() - len;
for (int i = 0; i <= max; i++) {
if (str.regionMatches(true, i, searchStr, 0, len)) {
return true;
}
}
return false;
}
}
- 创建灰度
- 点击确定后,灰度版本就创建成功了,页面会自动切换到灰度版本Tab.
-
灰度配置: 在灰度版本Tab中,可以"新增"灰度配置(主版本没有的配置项),也可以在主版本已有的配置项基础"修改"为灰度配置。
-
在此我们新增灰度配置:logging.level.root=warn
- 配置灰度规则: 切换到灰度规则Tab,点击新增规则按钮
- 在弹出框中灰度的IP下拉框会默认展示当前使用配置的机器列表,选择我们要灰度的IP:192.168.161.4,点击完成
- 切换到灰度实例列表Tab,就能看到192.168.161.4已经使用了灰度发布的配置项
- 需求实现效果说明: 在192.168.161.5的主机上,我们一直可以看到如下的nohup.out输出日志
但是在192.168.161.4的主机看不到上面的INFO级别的日志输出,因为我们通过设置灰度规则,指定了将192.168.161.4主机上的日志输出级别改为warn。warn的级别比INFO更高,所以在192.168.161.4看不到INFO级别的日志输出。
- 灰度发布:日志级别WARN只在192.168.161.4实例上发布,192.168.161.5我们没做发布
- 日志级别热更新:我们没有重启应用,应用的日志输出效果就发生了变化
放弃灰度与全量发布
如果我们在通过灰度发布的配置测试下来比较理想,符合预期,那么就可以操作全量发布。
全量发布的效果是:
-
灰度版本的配置会合并回主版本
-
主版本的配置会自动进行一次发布
-
在全量发布页面,可以选择是否保留当前灰度版本,默认为不保留。
如果我们通过灰度发布的配置测试下来不理想或者不需要了,可以点击放弃灰度
补充篇
客户端监听配置变化
正常情况下,我们获取Apollo配置的java客户端代码如下:
Config config = ConfigService.getAppConfig();
Integer defaultRequestTimeout = 200;
Integer requestTimeout = config.getIntProperty("requestTimeout", defaultRequestTimeout);
在某些场景下,应用需要在配置变化时获得通知,比如数据库连接的切换等,所以Apollo提供了监听配置变化的功能,Java示例如下:
//指定获取哪个命名空间下的配置
Config config = ConfigService.getConfig("FX.Hermes.Producer");
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()));
}
}
});
获取非properties格式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);
非yaml/yml格式的namespace: 获取时需要使用ConfigService.getConfigFile接口并指定Format,如ConfigFileFormat.XML。
String someNamespace = "test";
ConfigFile configFile = ConfigService.getConfigFile("test", ConfigFileFormat.XML);
String content = configFile.getContent();
控制Spring中注入的多个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 {}
与SpringBoot整合注意事项
Spring Boot除了支持上述两种集成方式以外,还支持通过application.properties/bootstrap.propertie
s来配置,该方式能使配置在更早的阶段注入
,比如使用@ConditionalOnProperty的场景或者是有一些spring-boot-starter在启动阶段就需要读取配置做一些事情(如dubbo-spring-boot-project),所以对于Spring Boot环境建议通过以下方式来接入Apollo(需要0.10.0及以上版本)。
- apollo.bootstrap.enabled配置会让apollo在bootstrap phase阶段就完成配置注入
# will inject 'application' namespace in bootstrap phase
apollo.bootstrap.enabled = true
- 注入非默认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
- 将Apollo配置加载提到初始化日志系统之前(1.2.0+)
- 从1.2.0版本开始,如果希望把日志相关的配置(如logging.level.root=info或logback-spring.xml中的参数)也放在Apollo管理,那么可以额外配置apollo.bootstrap.eagerLoad.enabled=true来使Apollo的加载顺序放到日志系统加载之前,更多信息可以参考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
Spring Placeholder的使用
Spring应用通常会使用Placeholder来注入配置,使用的格式形如 s o m e K e y : s o m e D e f a u l t V a l u e ,如 {someKey:someDefaultValue},如 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
Spring Annotation支持
Apollo同时还增加了几个新的Annotation来简化在Spring环境中的使用。
- @ApolloConfig
- 用来自动注入Config对象
- @ApolloConfigChangeListener
- 用来自动注册ConfigChangeListener
- @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;
}
}
SpringBoot项目已有配置迁移
很多情况下,应用可能已经有不少配置了,比如Spring Boot的应用,就会有bootstrap.properties/yml, application.properties/yml等配置。
在应用接入Apollo之后,这些配置是可以非常方便的迁移到Apollo的,具体步骤如下:
- 在Apollo为应用新建项目
- 在应用中配置好META-INF/app.properties
- 建议把原先配置先转为properties格式,然后通过Apollo提供的文本编辑模式全部粘帖到应用的application namespace,发布配置
- 如果原来格式是yml,可以使用YamlPropertiesFactoryBean.getObject转成properties格式
- 如果原来是yml,想继续使用yml来编辑配置,那么可以创建私有的application.yml namespace,把原来的配置全部粘贴进去,发布配置
- 需要apollo-client是1.3.0及以上版本
- 把原先的配置文件如bootstrap.properties/yml, application.properties/yml从项目中删除
- 如果需要保留本地配置文件,需要注意部分配置如
server.port
必须确保本地文件已经删除该配置项
- 如果需要保留本地配置文件,需要注意部分配置如
限于篇幅,下面剩余的部分大家自行阅读官方文档即可,写的非常清楚,我这边也就不再CV了
本地开发模式
本地开发模式
测试模式
测试模式