- 简介
配置中心现在基本上是大型互联网公司的标配,用于存储管理公司内部各个系统的配置,降低维护成本。本配置中心提供了:配置管理基本能力、配置发布回滚能力、配置更新推送能力、客户端配置缓存能力、对敏感配置设置访问权限能力。本配置中心的目标是让你能优雅的维护配置。
- 环境要求
- 服务端:jdk1.8
- 客户端:jdk1.8
- zookeeper
- MySQL
注意:本系统已经上传到maven中央库
- 演示环境
地址:http://configcenter.antframework.org:6220
超级管理员账号:admin 密码:123
普通管理员账号:normal 密码:123
- 技术交流和支持
欢迎进技术交流群一起讨论(加我微信zhong_xun_)。如果本项目对你有帮助,欢迎Star和Fork。
-
服务端:管理不同应用在不同环境中的配置,配置数据落地到MySQL数据库。为客户端提供http查询应用在指定环境中的配置。当一个应用在指定环境中的配置有了变更(增删改),则服务端会通过zookeeper通知客户端。
-
客户端:客户端刚启动时会通过http请求服务端读取当前应用在当前环境中的最新配置。如果从服务端读取失败,则客户端会尝试从本地缓存文件中读取配置,如果本地无缓存文件,则会抛出异常。客户端启动成功后,会监听配置变更事件。当监听到配置有变更时,客户端会再次通过http请求服务端读取最新配置,然后把最新配置保存到缓存文件,最后将最新配置和当前客户端中旧配置进行比较,将变化部分通知给应用。
-
zookeeper:仅仅作为通知工具,并不存储任何配置。当配置有变更,服务端会通知zookeeper,zookeeper接收到消息后会把消息分发给客户端,客户端收到消息后就会调用服务端读取最新配置。即使zookeeper宕机了,客户端也会每5分钟自动上服务端读取最新配置,对zookeeper是弱依赖。
-
应用树:应用树是各个应用之间的继承关系图,类似Java类的继承关系。应用可以多层继承,比如:customer(会员系统)--> core-domain(核心领域)--> common(公共配置)。应用的继承特性可以使不同应用之间共享配置。
-
环境树:环境树是各个环境之间的继承关系图,类似Java类的继承关系。环境可以多层继承,比如:featureTest1(特性测试环境1)--> test(测试环境)--> offline(线下环境)。环境的继承特性可以使一个应用在不同环境之间共享配置。不同集群在这里可以看成是不同的子环境,而不同集群间往往大部分配置是一样的,只有少数配置是不同的。有了继承特性,新增一个集群只需要进行继承,然后覆盖掉少数的不同配置就可以,而不需要从头开始建配置。
环境树存在的另一个原因:我们使用的环境变成树形结构可能更好。假设你在进行一个特性测试时,为了不影响公共测试环境,你需要单独部署一套独立的测试环境。可能你只修改了系统A,但系统A依赖了系统B和系统C,为了让系统A运行起来,你需要把系统B、C都部署起来。而系统B、C可能还会依赖其他系统,那么为了完整的测试你有可能会部署很多系统。如果你只是修改了系统A很小的一个功能点,为了测试再部署那么多套系统,那就显得得不偿失了。所以如果我们的环境是树形结构(有继承特性),那么你只需要在独立测试环境中部署系统A,当系统A需要调用系统B、C的服务时,只需要先判断当前环境中有没有B、C的服务,如果有则调用当前环境的服务;如果没有则调用父环境(公共测试环境)的服务。要实现这点还需要RPC框架的支持,支持也并不复杂:所有环境的服务注册到统一的一个服务注册中心,每个服务标识自己所在的是哪个环境,然后服务消费者在消费服务前,优先消费当前环境的服务,如果当前环境没有服务,则根据环境继承结构,依次往上查找可用的服务,直到找到为止,然后消费。利用RPC框架的扩展机制,可以完成上述功能。环境树怎么获取?可以直接调用配置中心获取。
-
私有:私有的配置只能被当前应用读取,其他应用无法读取,类似Java类的private访问级别字段。这种作用域可以保护配置不被暴露到其他应用。
-
可继承:可继承的配置既能被当前应用读取,也能被子应用读取,类似Java类的protected访问级别字段。这种作用域方便让同一个继承体系的应用共享配置,也让配置共享不扩大到所有应用。
-
公开:公开的配置能被所有应用读取,类似Java类的public访问级别字段。这种作用域能方便有些配置可以被所有应用读取,但又不适合将这种配置放到公共配置里。比如当前应用需要提供一个客户端给其他应用使用,而且客户端需要读取当前应用的一些配置,那么这些配置就可以作为公开配置。
访问权限只约束普通管理员,不会约束超级管理员。运维人员使用超级管理员账号把敏感配置的权限设置为“只读”或“无”,然后给开发人员创建普通管理员账号,让开发人员也能查看和修改非敏感配置(比如一些业务开关配置), 而不需要再通过运维人员。这样既提高了开发人员效率,也减轻了运维人员负担。
-
读写:普通管理员既能查看该配置项,也能修改该配置项。
-
只读:普通管理员只能查看该配置项,不能修改该配置项。
-
无:普通管理员既不能查看该配置项,也不能修改该配置项。
下载服务端。以下是集群部署架构图:
说明:
- 出于安全考虑,当外网(包括配置管理员)尝试通过http请求访问服务端/config/*路径时,nginx应该进行拦截;而客户端通过内网访问/config/*路径时,nginx应该允许访问。
- 服务端使用的springboot进行开发,直接命令启动下载好的jar包即可,无需部署tomcat。
- 有两种方式创建数据库表,根据具体情况选择其中一种方式即可:1、手动执行建表sql;2、让服务端拥有向数据库执行ddl语句权限,服务端第一次启动时会自动建表,无需手动执行sql。
- 服务端在启动时会在"/var/apps/"下创建日志文件,请确保服务端对该目录拥有写权限。
- 由于配置中心本身就是用来管理各个环境中的配置,所以大部分公司只需部署两套,一是线下环境配置中心(管理所有非线上环境配置);二是线上环境配置中心(管理线上环境配置)。
- 线下环境编码:offline,线上环境编码:online(可以根据各公司自己情况自己定义,这里只是根据我个人习惯推荐的两个编码)。
- 服务端http端口为6220。
启动服务端命令模板:
java -jar configcenter-1.4.0.RELEASE.jar --spring.profiles.active="online" --spring.datasource.url="数据库连接" --spring.datasource.username="数据库用户名" --spring.datasource.password="数据库密码" --meta.zk-urls="配置中心使用的zookeeper地址(IP:端口),如果存在多个zookeeper以英文逗号分隔"
比如我本地测试时启动命令:
java -jar configcenter-1.4.0.RELEASE.jar --spring.profiles.active="offline" --spring.datasource.url="jdbc:mysql://localhost:3306/configcenter-dev?useUnicode=true&characterEncoding=utf-8" --spring.datasource.username="root" --spring.datasource.password="root" --meta.zk-urls="localhost:2181"
读者也可以先看后面的“配置管理介绍”,再来看本部分的客户端介绍。
提供两种方式集成客户端:
- 直接集成客户端(非spring-boot应用)
- 通过starter进行集成(spring-boot应用)
客户端提供最核心也是最原子的能力。
<dependency>
<groupId>org.antframework.configcenter</groupId>
<artifactId>configcenter-client</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
客户端就是Java类,直接new就可以,只是需要传给它相应参数。一个应用可以创建多个客户端,每个客户端之间互不影响。
// 创建客户端
ConfigsContext configsContext = new ConfigsContext(
"http://localhost:6220", // 服务端地址
"customer", // 主体应用id
"dev", // 环境id
"/var/apps/configcenter"); // 缓存文件夹路径
// 获取会员系统的配置
Config customerConfig = configsContext.getConfig("customer");
// 现在就可以获取会员系统的所有配置项了(下面获取redis地址配置)
String redisHost = customerConfig.getProperties().getProperty("redis.host");
// 不仅可以获取会员系统的配置,还可以获取其他应用的配置,不过只能获取其他应用的公开配置,
// 因为当前主体应用为会员系统,现在是以会员系统为视角获取其他应用的配置
// 下面是获取账务系统的公开配置
Config accountConfig = configsContext.getConfig("account");
// 还可以注册配置变更监听器
customerConfig.getListenerRegistrar().register(new ConfigListener() {
@Override
public void onChange(List<ChangedProperty> changedProperties) {
for (ChangedProperty changedProperty : changedProperties) {
logger.info("监听到会员系统的配置有变更:{}", changedProperty);
}
}
});
// 开启配置变更监听功能
configsContext.listenConfigs();
// 系统正常运行...
// 当系统运行结束时,需关闭客户端释放相关资源
configsContext.close();
starter本质上还是依赖于上面介绍的客户端的能力,只不过根据spring-boot场景提供了更优雅的集成方式,也提供了更方便的功能。
<dependency>
<groupId>org.antframework.configcenter</groupId>
<artifactId>configcenter-spring-boot-starter</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
在应用的配置文件application.properties或application-xxx.properties中配置:
# 必填:应用id(如果未设置,则采用spring.application.name对应的值)
configcenter.app-id=customer
# 必填:服务端地址
configcenter.server-url=http://localhost:6220
# 选填:缓存目录(默认为:/var/apps/configcenter)
configcenter.cache-dir-path=/var/apps/configcenter
# 选填:是否开启监听配置变更事件(默认为开启)
configcenter.listen-configs.enable=true
# 选填:配置刷新周期(单位:秒。默认为5分钟刷新一次)
configcenter.refresh-period=300
# 选填:配置中心的配置优先于指定的配置源(默认为最低优先级)。可填入:commandLineArgs(命令行)、systemProperties(系统属性)、systemEnvironment(系统环境)、applicationConfigurationProperties(配置文件)等等
configcenter.prior-to=applicationConfigurationProperties
# 选填:如果想在日志中打印缓存文件路径,可以进行以下配置
logging.level.org.antframework.configcenter.client.support.ConfigRefresher=debug
可以通过spring的@Value注解、environment.getProperty(java.lang.String)获取配置,而不用直接使用客户端。也可以通过ConfigsContexts.getConfig(java.lang.String)获取配置。
// 通过@Value获取配置
@Value("redis.host")
private String redisHost;
@Autowired
private Environment environment;
public void doBiz() {
// 通过environment获取配置
String redisHostFromEnvironment = environment.getProperty("redis.host");
// 通过ConfigsContexts.getConfig(java.lang.String)获取配置
Config config = ConfigsContexts.getConfig("customer");
String redisHostFromConfig = config.getProperties().getProperty("redis.host");
}
可以监听当前应用的配置变更事件:
// 监听当前应用的配置变更事件
@ConfigListener
public class MyConfigListener {
// 监听所有配置
@ListenConfigChanged(prefix = "")
public void listenAll(List<ChangedProperty> changedProperties) {
// TODO 具体业务代码
}
// 监听redis配置(prefix表示需要监听的配置前缀。当以“redis.”开头的配置项被修改时,
// 被修改的配置会作为入参调用本方法。比如redis.host、redis.port等被修改时都会调用本方法)
@ListenConfigChanged(prefix = "redis")
public void listenPool(List<ChangedProperty> changedProperties) {
// TODO 具体业务代码
}
// 监听具体某一个配置项(注意:入参不再是List<ChangedProperty>,而是ChangedProperty)
@ListenConfigChanged(prefix = "redis.host")
public void listenPool(ChangedProperty changedProperty) {
// TODO 具体业务代码
}
}
也可以监听其他应用的公开配置变更事件:
// 监听账务系统的公开配置变更事件
@ConfigListener(appId = "account")
public class MyConfigListener {
// 监听所有配置
@ListenConfigChanged(prefix = "")
public void listenAll(List<ChangedProperty> changedProperties) {
// TODO 具体业务代码
}
}
进入服务端地址模板:http://IP地址:6220
以下截图有部分未更新,以实际使用为准。
4.7 应用有继承关系,而且可以多层继承,类似于java类的单继承,这样的设计可以让我们的配置项的暴露控制在合理的范围。比如下图中的customer(会员系统)--> core-domain(核心领域)--> common(公共配置)
4.13 点击上图中的新增配置按钮,进行创建配置key。配置key是所有环境共享的,可以指定配置key的作用域:私有、可继承、公开(类似于java类的字段作用域:private、protected、public)
4.14 应用会继承父应用的可继承和公开作用域的配置key。比如会员系统会继承core-domain(核心领域)、common(公共配置)的配置key
4.15 点击上图中的环境,进入对应环境的配置管理,可进行配置修改
4.16 修改配置后,点击"发布修改"按钮,进行发布(配置变更消息会自动推送给应用)
4.19 在发布历史页面可看到所有的发布记录、每次发布变更的配置和所有配置
4.22 点击上图的新增按钮,进行创建管理员。管理员分为两种:超级管理员和普通管理员。超级管理员拥有所有权限可以管理所有应用,普通管理员只能管理被分配给他的应用