配置管理中心
一、为什么需要配置管理中心
随着程序功能的日益复杂,程序的配置日益增多:各种功能的开关、参数的配置、服务器的地址……
并且对配置的期望也越来越高,配置修改后实时生效,灰度发布,分环境、分集群管理配置,完善的权限、审核机制……
并且随着采用分布式的开发模式,项目之间的相互引用随着服务的不断增多,相互之间的调用复杂度成指数升高,每次投产或者上线新的项目时苦不堪言,因此需要引用配置中心治理。
二、配置管理中心对比
1、功能特性
2、兼容性
3、可用性和易用性
4、Apollo与Nacos对比
结论:选择Apollo
[官网地址] https://github.com/ctripcorp/apollo
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
三、Apollo
1、基础模型
如下即是Apollo的基础模型:
用户在配置中心对配置进行修改并发布
配置中心通知Apollo客户端有配置更新
Apollo客户端从配置中心拉取最新的配置、更新本地配置并通知到应用
2、架构模块
上图简要描述了Apollo的总体设计,我们可以从下往上看:
Config Service提供配置的读取、推送等功能,服务对象是Apollo客户端
Admin Service提供配置的修改、发布等功能,服务对象是Apollo Portal(管理界面)
Config Service和Admin Service都是多实例、无状态部署,所以需要将自己注册到Eureka中并保持心跳
在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进程中
总结:重要的组件有apollo-configservice、apollo-adminservice、apollo-portal、apollo-client
3、工作流程图
用户在Portal操作配置发布Portal调用Admin Service的接口操作发布Admin Service发布配置后,发送ReleaseMessage给各个Config ServiceConfig Service收到ReleaseMessage后,通知对应的客户端
4、客户端设计
上图简要描述了Apollo客户端的实现原理:
客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现)
客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
这是一个fallback机制,为了防止推送机制失效导致配置不更新
客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property:
apollo.refreshInterval
来覆盖,单位为分钟。
客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
客户端会把从服务端获取到的配置在本地文件系统缓存一份
在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知
四、Apollo实战
1、部署Apollo
Apollo的部署官网有非常详细的教程,这里不再详细描述,只提本地化的东西。
1.1数据库
apollo的数据是一个portal数据库配合多个不同环境的config数据库使用,portal管理多个环境。部署成功后如果是多个环境需要修改数据库配置。
修改apolloportaldb的serverconfig表中的apollo.portal.envs和configView.memberOnly.envs,默认支持四个环境(DEV,UAT,FAT,PRO),逗号隔开,更多环境的配置参考官方Wiki中的《部署&开发遇到的常见问题》
修改apolloconfigdb的serverconfig表中的eureka.service.url,多个地址用逗号分隔
1.2应用
应用暂时无需修改,如果需要修改,参考官方Wiki中的《分部署部署指南》
2、使用Apollo
2.1环境地址配置
方式一:通过环境配置文件/opt/data/settings.properties
apollo.meta=http://127.0.0.1:8080复制
方式二:通过工程内配置文件apollo-env.properties
dev.meta=http://127.0.0.1:8080复制
2.2环境应用配置
通过环境配置文件/opt/data/settings.properties
方式一:通过启动参数
-Denv=YOUR-ENVIRONMENT复制
方式二:通过环境配置文件/opt/data/settings.properties
env=DEV复制
2.3集群配置(可选)
通过环境配置文件/opt/data/settings.properties
idc=xxx复制
2.4缓存配置
默认缓存路径/opt/data/cache,无需修改
2.5AppId配置
通过application.yml
app:
id: 100000001复制
2.6引入依赖
<!-- apollo support -->
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.4.0</version>
</dependency>复制
2.7客户端使用
客户端使用默认支持很多种方式,api,spring,springboot,dubbo等等,推荐参考官网案例即可
[案列] https://github.com/ctripcorp/apollo-use-cases
此处选择springboot说明
2.7.1配置
apollo:
bootstrap:
#设置apollo在启动阶段就加载配置
enabled: true
#注入namespace的配置
namespaces: application,LCGLB.application,LCGLB.jwt,LCGLB.aeskey,datasources.yml,workflow.yml,sleuth.yml
#将apollo配置加载提到初始化日志系统之前
eagerLoad:
enabled: true复制
2.7.2配置解析器
解决读取复杂yml读取失败的问题
启动类中注册Bean
@Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer () {
return new PropertySourcesPlaceholderConfigurer();
}复制
2.7.3配置文件
/**
* jwt配置文件读取类
*
* @author zhangwy
* @date 2019-03-26
*
* */
@Configuration
@ConfigurationProperties(ignoreInvalidFields=true, ignoreUnknownFields=true, prefix="jwt")
@EnableApolloConfig
@Component("jwtConfig")
@RefreshScope
@Data
public class JwtConfig {
private String secretkey;
}复制
2.7.4配置自定刷新
/**
* @author zhangwy
*/
@Component
public class SpringBootApolloRefreshConfig {
private static final Logger logger = LoggerFactory.getLogger(SpringBootApolloRefreshConfig.class);
private final RefreshScope refreshScope;
public SpringBootApolloRefreshConfig(final RefreshScope refreshScope) {
this.refreshScope = refreshScope;
}
@Autowired
DataSourceProps dataSourceProps;
@Autowired
WorkflowConfig workflowConfig;
@Autowired
SamplerLocalProperties samplerLocalProperties;
@ApolloConfigChangeListener(value = { ConfigConsts.NAMESPACE_APPLICATION, "LCGLB.jwt", "LCGLB.aeskey", "LCGLB.application", "datasources.yml", "workflow.yml", "sleuth.yml" })
public void onChange(ConfigChangeEvent changeEvent) {
logger.info("dataSourceProps before refresh {}", dataSourceProps.toString());
logger.info("workflowConfig before refresh {}", workflowConfig.toString());
refreshScope.refresh("jwtConfig");
refreshScope.refresh("aesConfig");
refreshScope.refresh("dataSourceProps");
refreshScope.refresh("workflowConfig");
refreshScope.refresh("samplerLocalProperties");
logger.info("dataSourceProps after refresh {}", dataSourceProps.toString());
logger.info("workflowConfig after refresh {}", workflowConfig.toString());
}
}复制
3、开放平台
[参考官网] https://github.com/ctripcorp/apollo/wiki/Apollo%E5%BC%80%E6%94%BE%E5%B9%B3%E5%8F%B0