【配置中心----Apollo】Apollo的介绍及使用方式
环境SpringBoot 2一、Apollo简介项目组最近的项目都是使用springcloud微服务开发,整个微服务框架中分布式的系统服务、集群等等都非常的多。每一个服务都有着自己的配置(包括参数配置、服务器地址配置、功能开关等都能),当配置需要修改的时候就显得异常的麻烦,传统的通过配置文件、数据库配置完全不能满足要求。在这种情况下,诞生了很多的统一配置的服务,虽然springcl...
环境SpringBoot 2
一、Apollo简介
项目组最近的项目都是使用springcloud微服务开发,整个微服务框架中分布式的系统服务、集群等等都非常的多。
每一个服务都有着自己的配置(包括参数配置、服务器地址配置、功能开关等都能),当配置需要修改的时候就显得异常的麻烦,传统的通过配置文件、数据库配置完全不能满足要求。
在这种情况下,诞生了很多的统一配置的服务,虽然springcloud有自己的config配置中心,但是和携程的apollo相比还是太弱了,对比之后我们使用了Apollo。
Apollo(阿波罗)是携程框架部门研发的开源配置管理中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性。
github上携程开发人员对Apollo做了特别细致的介绍,但是对于我们只使用springboot进行开发的项目来说,其实只用到了里面一小部分内容。
因此我主要将Apollo和Springboot相关的内容整理出来,本文首先对Apollo进行QuickStart简单入门介绍以及基于Springboot框架的使用。
1.1 Apollo配置中心基础模型
- 开发人员在配置中心对某些应用的配置(就是一些键值对的信息)进行修改
- 配置中心通知客户端进行配置更新(需要注册监听事件)
- Apollo客户端(应用程序)从配置中心拉取更新最新配置
1.2 Apollo的总体设计
上图是Apollo的总体设计,从下往上看:
- ConfigService(Apollo集成在一个jar包中)提供配置的读取,推送功能,Apollo客户端(你的应用程序)从这儿读取配置。
- AdminService(Apollo集成在一个jar包中)提供配置的修改、发布功能,Apollo Portal(开发人员登录的修改配置的系统)调用该服务。
- ConfigService和AdminService都是多实例服务,需要将它们注册到Eureka中。
- 在Eureka之上有一层MetaServer用于封装Eureka的服务发现接口。
- Client和Portal通过域名访问MetaServer获取ConfigService和AdminService的服务列表(IP+Port),然后直接通过套接字访问服务。
- 为了简化部署,实际上会把Configservice、Eureka和MetaServer部署在同一个JVM进程中。
1.3 Apollo配置分类
因为对于不同的应用,在不同的环境(开发、测试)、不同的集群(华东、华北)、不同的命名空间(例如springboot的application.property文件、你自己自定义的myappconfig.property),配置都有可能不同。
所以Apollo支持了4个维度管理Key-Value格式的配置(这些都是可以配置的):
- application(应用)
- 在springboot的application.property定义appid这个key的value,标识该类型的应用。
- environment(4种环境)
- DEV(开发环境)
- FAT(功能测试)
- UAT(验收测试)
- PRO(生产环境)
- cluster(集群)
- namespace(命名空间,其实就是某个应用的不同配置文件)
同时,Apollo基于开源模式开发,开源地址:https://github.com/ctripcorp/apollo
1.4 Apollo特性
正是基于配置的特殊性,所以Apollo从设计之初就立志于成为一个有治理能力的配置管理平台,目前提供了以下的特性:
- 统一管理不同环境、不同集群的配置
- Apollo 提供了一个统一界面集中式管理不同环境(environment)、不同集群(cluster)、不同命名空间(namespace)的配置。
- 同一份代码部署在不同的集群,可以有不同的配置,比如zk的地址等
- 通过命名空间(namespace)可以很方便的支持多个不同应用共享同一份配置,同时还允许应用对共享的配置进行覆盖
- 配置修改实时生效(热发布)
- 用户在Apollo修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并通知到应用程序
- 版本发布管理
- 所有的配置发布都有版本概念,从而可以方便地支持配置的回滚
- 灰度发布
- 支持配置的灰度发布,比如点了发布后,只对部分应用实例生效,等观察一段时间没问题后再推给所有应用实例
- 权限管理、发布审核、操作审计
- 应用和配置的管理都有完善的权限管理机制,对配置的管理还分为了编辑和发布两个环节,从而减少人为的错误。
- 所有的操作都有审计日志,可以方便的追踪问题
- 客户端配置信息监控
- 可以在界面上方便地看到配置在被哪些实例使用
- 提供Java和.Net原生客户端
- 提供了Java和.Net的原生客户端,方便应用集成
- 支持Spring Placeholder, Annotation和Spring Boot的ConfigurationProperties,方便应用使用(需要Spring 3.1.1+)
- 同时提供了Http接口,非Java和.Net应用也可以方便的使用
- 提供开放平台API
- Apollo自身提供了比较完善的统一配置管理界面,支持多环境、多数据中心配置管理、权限、流程治理等特性。
- 不过Apollo出于通用性考虑,对配置的修改不会做过多限制,只要符合基本的格式就能够保存。
- 在我们的调研中发现,对于有些使用方,它们的配置可能会有比较复杂的格式,而且对输入的值也需要进行校验后方可保存,如检查数据库、用户名和密码是否匹配。
- 对于这类应用,Apollo支持应用方通过开放接口在Apollo进行配置的修改和发布,并且具备完善的授权和权限控制
- 部署简单
- 配置中心作为基础服务,可用性要求非常高,这就要求Apollo对外部依赖尽可能地少
- 目前唯一的外部依赖是MySQL,所以部署非常简单,只要安装好Java和MySQL就可以让Apollo跑起来
- Apollo还提供了打包脚本,一键就可以生成所有需要的安装包,并且支持自定义运行时参数
1.5 配置获取规则
【本节仅当应用自定义了集群或namespace才需要,如无相关需求,可以跳过本节】
在有了cluster概念后,配置的规则就显得重要了。
比如应用部署在A机房,但是并没有在Apollo新建cluster,这个时候Apollo的行为是怎样的?
或者在运行时指定了cluster=SomeCluster,但是并没有在Apollo新建cluster,这个时候Apollo的行为是怎样的?
接下来就来介绍一下配置获取的规则。
1.5.1 应用自身配置的获取规则
当应用使用下面的语句获取配置时,我们称之为获取应用自身的配置,也就是应用自身的application namespace的配置。
Config config = ConfigService.getAppConfig();
对这种情况的配置获取规则,简而言之如下:
- 首先查找运行时cluster的配置(通过apollo.cluster指定)
- 如果没有找到,则查找数据中心cluster的配置
- 如果还是没有找到,则返回默认cluster的配置
图示如下:
所以如果应用部署在A数据中心,但是用户没有在Apollo创建cluster,那么获取的配置就是默认cluster(default)的。
如果应用部署在A数据中心,同时在运行时指定了SomeCluster,但是没有在Apollo创建cluster,那么获取的配置就是A数据中心cluster的配置,如果A数据中心cluster没有配置的话,那么获取的配置就是默认cluster(default)的。
Apollo客户端会把从服务端获取到的配置在本地文件系统缓存一份,当去服务器读取配置失败时,会使用本地缓存的。
Mac/Linux: /opt/data/{appId}/config-cache
Windows: C:\opt\data{appId}\config-cache
确保目录存在,且应用有读写权限。
1.5.2 公共组件配置的获取规则
以FX.Hermes.Producer为例,hermes producer是hermes发布的公共组件。当使用下面的语句获取配置时,我们称之为获取公共组件的配置。
Config config = ConfigService.getConfig("FX.Hermes.Producer");
对这种情况的配置获取规则,简而言之如下:
- 首先获取当前应用下的FX.Hermes.Producer namespace的配置
- 然后获取hermes应用下FX.Hermes.Producer namespace的配置
- 上面两部分配置的并集就是最终使用的配置,如有key一样的部分,以当前应用优先
图示如下:
通过这种方式,就实现了对框架类组件的配置管理,框架组件提供方提供配置的默认值,应用如果有特殊需求,可以自行覆盖。
1.6 总体设计
上图简要描述了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进程中
为什么选择Eureka
为什么我们采用Eureka作为服务注册中心,而不是使用传统的zk、etcd呢?我大致总结了一下,有以下几方面的原因:
- 它提供了完整的Service Registry和Service Discovery实现
- 首先是提供了完整的实现,并且也经受住了Netflix自己的生产环境考验,相对使用起来会比较省心。
- 和Spring Cloud无缝集成
- 我们的项目本身就使用了Spring Cloud和Spring Boot,同时Spring Cloud还有一套非常完善的开源代码来整合Eureka,所以使用起来非常方便。
- 另外,Eureka还支持在我们应用自身的容器中启动,也就是说我们的应用启动完之后,既充当了Eureka的角色,同时也是服务的提供者。这样就极大的提高了服务的可用性。
- 这一点是我们选择Eureka而不是zk、etcd等的主要原因,为了提高配置中心的可用性和降低部署复杂度,我们需要尽可能地减少外部依赖。
- Open Source
- 最后一点是开源,由于代码是开源的,所以非常便于我们了解它的实现原理和排查问题。
1.7 客户端设计
上图简要描述了Apollo客户端的实现原理:
- 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。
- 客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
- 这是一个fallback机制,为了防止推送机制失效导致配置不更新
- 客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
- 定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property:
apollo.refreshInterval
来覆盖,单位为分钟。
- 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
- 客户端会把从服务端获取到的配置在本地文件系统缓存一份
- 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
- 应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知
1.7 配置更新推送实现
前面提到了Apollo客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。
长连接实际上我们是通过Http Long Polling实现的,具体而言:
- 客户端发起一个Http请求到服务端
- 服务端会保持住这个连接30秒
- 如果在30秒内有客户端关心的配置变化,被保持住的客户端请求会立即返回,并告知客户端有配置变化的namespace信息,客户端会据此拉取对应namespace的最新配置
- 如果在30秒内没有客户端关心的配置变化,那么会返回Http状态码304给客户端
- 客户端在服务端请求返回后会自动重连
考虑到会有数万客户端向服务端发起长连,在服务端我们使用了async servlet(Spring DeferredResult)来服务Http Long Polling请求。
1.8 可用性考虑
配置中心作为基础服务,可用性要求非常高,下面的表格描述了不同场景下Apollo的可用性:
场景 | 影响 | 降级 | 原因 |
---|---|---|---|
某台config service下线 | 无影响 | Config service无状态,客户端重连其它config service | |
所有config service下线 | 客户端无法读取最新配置,Portal无影响 | 客户端重启时,可以读取本地缓存配置文件 | |
某台admin service下线 | 无影响 | Admin service无状态,Portal重连其它admin service | |
所有admin service下线 | 客户端无影响,portal无法更新配置 | ||
某台portal下线 | 无影响 | Portal域名通过slb绑定多台服务器,重试后指向可用的服务器 | |
全部portal下线 | 客户端无影响,portal无法更新配置 | ||
某个数据中心下线 | 无影响 | 多数据中心部署,数据完全同步,Meta Server/Portal域名通过slb自动切换到其它存活的数据中心 |
二、Apollo快速开始
携程的开发人员为了让大家几分钟快速上手Apollo配置中心,已经准备了一套安装包,按照教程马上就能部署好了
注意windows环境要安装能运行bash脚本的软件如gitbash QuickStart github地址。
安装包在不同的端口安装了配置中心的不同服务:
- http://localhost:8070 Portal客户端的服务
- http://localhost:8080 ConfigService、MetaServer、Eureka服务
- http://localhost:8090 可能是AdminService服务
2.1 打开 http://localhost:8070 进入配置中心就可以对各种应用进行配置(修改键值对)
进入配置中心就可以新建项目(对应你的应用)
比如你有一个叫KiddService的接口,可以给它起一个唯一标识的应用Id(这里我叫kiddApp),提交之后在主页就可以看到了。
进入该项目的配置,先发布后配置或先配置后发布都行,这样你的应用就可以从配置中心拉取配置了。
编辑配置,其实就是添加或修改键值对
2.2 Springboot客户端连接Apollo配置中心
使用Springboo客户端连接Apollo特别的简单,只需要几步:
-
必要的设置
- AppId,在application.properties文件加入指定appId,app.id=YOUR-APP-ID。
- Apollo Meta Server,前面说过,这是对Eureka的一个封装服务,主要是用来得到ConfigServer的地址。在application.properties或bootstrap.properties中指定apollo.meta=http://config-service-url。
- 本地配置缓存路径,确保目录存在
- Mac/Linux: /opt/data/{appId}/config-cache
- Windows: C:\opt\data{appId}\config-cache
-
可选设置
- Environment(上面说的四种环境)
- 在Java程序启动脚本中,可以指定-Denv=YOUR-ENVIRONMENT
- 还可以通过操作系统的System Environment ENV来指定
- 通过配置文件来指定env=YOUR-ENVIRONMENT;(windows C:\opt\settings\server.properties)(linux /opt/settings/server.properties)
- Cluster(集群)
- 在Java程序启动脚本中,可以指定-Dapollo.cluster=SomeCluster
- 过Spring Boot的配置文件,apollo.cluster=SomeCluster
- 可以在server.properties配置文件中指定idc=xxx
- Environment(上面说的四种环境)
例如在我的quickstart测试环境中我在application.properties文件中只要如下配置就行了:
server.port=9017
app.id=apolloapp
apollo.meta=http://127.0.0.1:8080
Maven Dependency(maven的依赖)
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.0.0</version>
</dependency>
获取本应用的配置
在springboot下获取配置的方法有很多种,以我开发的KiddService为例:
Config config = ConfigService.getAppConfig(); //config instance is singleton for each namespace and is never null
String someKey = "key1";
String someDefaultValue = "none";
String value = config.getProperty(someKey, someDefaultValue);
@Configuration
@EnableApolloConfig
public class AppConfig {
@Bean
public TestJavaConfigBean javaConfigBean() {
return new TestJavaConfigBean();
}
}
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;
}
}
一般所有的配置放在一个bean里面就可以了
更多推荐
所有评论(0)