这是我参与更文挑战的第16天
apollo配置中心
1.历史
Apollo是携程框架部门研发的分布式陪配置中心,能够集中化管理应用,即不同环境、不同集群的配置。配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
服务端基于spring Boot和spring Cloud开发,打包后可以直接运行,不需要额外安装Tomcat等应用容器。
Java客户端不依赖任何框架,能够运行于所有Java运行时环境,同时对Spring/Spring Boot环境也有较好的支持。
.Net客户端不依赖任何框架,能够运行于所有.Net运行时环境。
Apollo(阿波罗)是携程框架部门研发的开源配置管理中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性。
Apollo支持4个维度管理Key-Value格式的配置:
application
(应用)environment
(环境)cluster
(集群)namespace
(命名空间)
1.链接
2.部署
用于多个微服务配置文件的统一管理。
portal
:后台配置管理页面;
config
:提供配置的读取、推送等功能;
admin
:提供配置的修改、发布等功能
- 使用官方的网站已经发版好的包直接构建会比较快些,即发行版。在
github
上有发行版,在gitee
上可能找不到。
# 将下面三个文件下载拷贝到linux服务器上
apollo-portal-1.7.1-github, apollo-configservice-1.7.1-github, apollo-adminservice-1.7.1-github
# 修改数据库连接地址, 在configservice中, 修改jdbc后面的地址即可。并且修改账户
vim application-github.properties
# 使用apollo的sql创建相应的数据, 在指导文件中的apollo-quick-start中, 先使用
# apolloconfigdb.sql创建数据库
apolloconfigdb.sql, apolloportaldb.sql
# 创建之后启动configservice的jar包, 此时可能会报错, 小坑
The server time zone value 'xxxxx(一串乱码)',
# 修改config中的编码即可, 添加如下字段
characterEncoding=utf8&serverTimezone=Asia/Shanghai
# 之后还会报一个错,这是因为eureka没有起来, 他自己注册自己, 没有办法注册, 在eureka启动
# 之后, 自己注册自己成功即可, 即出现Started Eureka Server字段
java -jar xxx.jar
com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
# 之后启动adminservice
java -jar xxx.jar
# 由于防火墙的原因, 导致eureka网页不能访问
# 使用命令查看防火墙状态
systemctl status firewalld
# 查看相应端口是否有访问权限
firewall-cmd --query-port=8080/tcp
# 如果没有, 添加相应的端口权限, 永久生效还需要添加 --permanent 参数
firewall-cmd --permanent --add-port=80/tcp
# 也可以移除
firewall-cmd --permanent --remove-port=8080/tcp
# 修改配置后要重启防火墙
firewall-cmd --reload
# 参数解释
1、firwall-cmd:是Linux提供的操作firewall的一个工具;
2、--permanent:表示设置为持久;
3、--add-port:标识添加的端口;
# 修改portal的配置文件, 同样是数据库的地址\用户\密码\编码风格
application-github.properties
# 修改apollo-env.properties, 只留下开发的环境, 剩余的是预发\生产等环境
local.meta=http://localhost:8080
dev.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
# 启动之后在网页访问 ip+端口号, 账户是apollo, 密码admin
# 如果要修改项目管理, 此时只能到数据库中修改, 网页没有添加或者修改的
# 在ApolloPortalDB数据库中organizations添加
复制代码
之后的就是对项目的CRUD等等。然后配置是一组key-value
形式
1.应用
spring-boot项目
依赖:
<!-- https://mvnrepository.com/artifact/com.ctrip.framework.apollo/apollo-client -->
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.7.0</version>
</dependency>
复制代码
在application.properties
中添加下面配置
app.id=001
spring.application.name=provider
apollo.meta=http://192.168.40.131:8080
apollo.bootstrap.enabled=true
apollo.bootstrap.namespaces=application
复制代码
启动项目直接访问地址即可。
但是使用bootstrap.yml
的配置是无法启动的, 配置文件无法生效,导致无法从apollo
上拉取服务, 不知道啥原因。使用application.yml
报错无法从appId:1
上拉取服务, 而我们的app.id
是001, 不匹配肯定拉取不到。这是apollo
的决定的。详细报错如下图
Sync config failed, will retry. Repository class com.ctrip.framework.apollo.internals.RemoteConfigRepository, reason: Load Apollo Config failed - appId: 1
复制代码
apollo
的app.id
不能以0
开头
有时候, 更改不需要马上生效, 我们需要手动控制配置, 此时就不能使用上述全局的监听。 解决方法如下apollo
类
@Bean
public void config() {
// config instance is singleton for each namespace and is never null
Config config = ConfigService.getAppConfig();
config.addChangeListener(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()));
}
});
}
复制代码
3.原理
-
针对一组服务,会有一个
app.id
用于区分不同的服务组,即唯一标识。下图所示:app.id=mall
,这一组的微服务名称是mall
,不同的服务组,app.id
肯定是不一样的了。那么服务配置放到哪里?像spring Cloud是放到git上的,但是缺陷是没有一整套流程管理,如对配置文件的CRUD,且没有相应的UI
界面进行操作,只能使用git的客户端进行操作。 -
apollo
和nacos
则将配置文件放入了mysql
中。当配置文件被修改,apollo
会通过MQ
进行通知应用程序,配置文件已修改,至于服务怎么处理,此时就不是配置中心该去操心的事情了。 -
MQ
集成了BUS
组件,BUS
底层又集成了EIP
(企业级消息集成)
问题1:如果项目启动的时候,因为一些外力因素,比如网路延时等,导致配置中心没有连接上,该怎么处理?(高可用)
在项目启动的时候,设置一个默认值并缓存,然后在后续连接上apollo
的时候将最后一次更新的值放入缓存中。
然而缓存带来的问题就是, 服务down后, 客户仍然可以访问, 并且可以拿到值
问题2:如果一直连不上,一直缓存,怎么解决?
在apollo
中,每一个服务节点,都有不同的角色。
admin
: 用来对所有的配置文件进行crudportal
:相当于一个前端UI
界面config
:config server
相当与一个推销员,在config
发生变化时,会向应用服务推送消息。然后应用服务在收到消息后,会在config
服务中找相应的配置文件。在config
中集成了eureka
的注册发现功能
小结:所以一般高可用,需要配置admin
和config
。
apollo
的总体设计
上图简要描述了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
进程中
1.apollo
基础模型
- 用户在配置中心对配置进行修改并发布
- 配置中心通知Apollo客户端有配置更新
- Apollo客户端从配置中心拉取最新的配置、更新本地配置并通知到应用
2.客户端设计
上图简要描述了Apollo客户端的实现原理:
-
客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。
-
长连接实际上我们是通过
Http Long Polling
实现的,具体而言:- 客户端发起一个
Http
请求到服务端 - 服务端会保持住这个连接60秒
- 如果在60秒内有客户端关心的配置变化,被保持住的客户端请求会立即返回,并告知客户端有配置变化的
namespace
信息,客户端会据此拉取对应namespace
的最新配置 - 如果在60秒内没有客户端关心的配置变化,那么会返回
Http
状态码304给客户端
- 如果在60秒内有客户端关心的配置变化,被保持住的客户端请求会立即返回,并告知客户端有配置变化的
- 客户端在收到服务端请求后会立即重新发起连接,回到第一步
考虑到会有数万客户端向服务端发起长连,在服务端我们使用了
async servlet(Spring DeferredResult)
来服务Http Long Polling
请求。 - 客户端发起一个
-
-
客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
- 这是一个
fallback
机制,为了防止推送机制失效导致配置不更新 - 客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 – Not Modified
- 定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property:
apollo.refreshInterval
来覆盖,单位为分钟。
- 这是一个
-
客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
-
客户端会把从服务端获取到的配置在本地文件系统缓存一份
- 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
-
应用程序从Apollo客户端获取最新的配置、订阅配置更新通知
3.可用性考虑
配置中心作为基础服务,可用性要求非常高,下面的表格描述了不同场景下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 自动切换到其它存活的数据中心 |