背景
随着程序功能的日益复杂,程序的配置日益增多:各类功能的开关、参数的配置、服务器的地址等等。html
对程序配置的指望值也愈来愈高:配置修改后实时生效,灰度发布,分环境、分集群管理配置,完善的权限、审核机制等等。java
在这样的大环境下,传统的经过配置文件、数据库等方式已经愈来愈没法知足开发人员对配置管理的需求。mysql
Apollo 配置中心应运而生!Apollo - 一个可靠的配置管理系统。git
Apollo 介绍
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,可以集中化管理应用不一样环境、不一样集群的配置,配置修改后可以实时推送到应用端,而且具有规范的权限、流程治理等特性,适用于微服务配置管理场景。服务端基于 Spring Boot 和 Spring Cloud 开发,打包后能够直接运行,不须要额外安装 Tomcat 等应用容器。github
Apollo 支持 4 个维度管理 Key-Value 格式的配置:web
- application (应用)
- environment (环境)
- cluster (集群)
- namespace (命名空间 Namespace 是配置项的集合,相似于一个配置文件的概念)
同时,Apollo 基于开源模式开发,开源地址:https://github.com/ctripcorp/apollospring
官网文档:https://github.com/ctripcorp/apollo/wiki/Quick-Startsql
演示环境(Demo):shell
- http://106.54.227.205/
- 帐号/密码:apollo/admin
上图是Apollo配置中心中一个项目的配置首页数据库
- 在页面左上方的环境列表模块展现了全部的环境和集群,用户能够随时切换。
- 页面中央展现了两个namespace(application和FX.apollo)的配置信息,默认按照表格模式展现、编辑。用户也能够切换到文本模式,以文件形式查看、编辑。
- 页面上能够方便地进行发布、回滚、灰度、受权、查看更改历史和发布历史等操做。
Apollo 核心概念
- application (应用)
- 这个很好理解,就是实际使用配置的应用,Apollo客户端在运行时须要知道当前应用是谁,从而能够去获取对应的配置
- 每一个应用都须要有惟一的身份标识 -- appId,咱们认为应用身份是跟着代码走的,因此须要在代码中配置,具体信息请参见Java客户端使用指南。
- environment (环境)
- 配置对应的环境,Apollo客户端在运行时须要知道当前应用处于哪一个环境,从而能够去获取应用的配置
- 咱们认为环境和代码无关,同一份代码部署在不一样的环境就应该可以获取到不一样环境的配置
- 因此环境默认是经过读取机器上的配置(server.properties中的env属性)指定的,不过为了开发方便,咱们也支持运行时经过System Property等指定,具体信息请参见Java客户端使用指南。
- cluster (集群)
- 一个应用下不一样实例的分组,好比典型的能够按照数据中心分,把上海机房的应用实例分为一个集群,把北京机房的应用实例分为另外一个集群。
- 对不一样的cluster,同一个配置能够有不同的值,如zookeeper地址。
- 集群默认是经过读取机器上的配置(server.properties中的idc属性)指定的,不过也支持运行时经过System Property指定,具体信息请参见Java客户端使用指南。
- namespace (命名空间)
- 一个应用下不一样配置的分组,能够简单地把namespace类比为文件,不一样类型的配置存放在不一样的文件中,如数据库配置文件,RPC配置文件,应用自身的配置文件等
- 应用能够直接读取到公共组件的配置namespace,如DAL,RPC等
- 应用也能够经过继承公共组件的配置namespace来对公共组件的配置作调整,如DAL的初始数据库链接数
Apollo 特性
- 统一管理不一样环境、不一样集群的配置
- Apollo提供了一个统一界面集中式管理不一样环境(environment)、不一样集群(cluster)、不一样命名空间(namespace)的配置。
- 同一份代码部署在不一样的集群,能够有不一样的配置,好比zk的地址等
- 经过命名空间(namespace)能够很方便的支持多个不一样应用共享同一份配置,同时还容许应用对共享的配置进行覆盖
- 配置界面支持多语言(中文,English)
- 配置修改实时生效(热发布)
- 用户在Apollo修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并通知到应用程序。
- 版本发布管理
- 全部的配置发布都有版本概念,从而能够方便的支持配置的回滚。
- 灰度发布
- 支持配置的灰度发布,好比点了发布后,只对部分应用实例生效,等观察一段时间没问题后再推给全部应用实例。
- 权限管理、发布审核、操做审计
- 应用和配置的管理都有完善的权限管理机制,对配置的管理还分为了编辑和发布两个环节,从而减小人为的错误。
- 全部的操做都有审计日志,能够方便的追踪问题。
- 客户端配置信息监控
- 能够方便的看到配置在被哪些实例使用
- 提供Java和.Net原生客户端
- 提供了Java和.Net的原生客户端,方便应用集成
- 支持Spring Placeholder,Annotation和Spring Boot的ConfigurationProperties,方便应用使用(须要Spring 3.1.1+)
- 同时提供了Http接口,非Java和.Net应用也能够方便的使用
- 提供开放平台API
- Apollo自身提供了比较完善的统一配置管理界面,支持多环境、多数据中心配置管理、权限、流程治理等特性。
- 不过Apollo出于通用性考虑,对配置的修改不会作过多限制,只要符合基本的格式就可以保存。
- 在咱们的调研中发现,对于有些使用方,它们的配置可能会有比较复杂的格式,如xml, json,须要对格式作校验。
- 还有一些使用方如DAL,不只有特定的格式,并且对输入的值也须要进行校验后方可保存,如检查数据库、用户名和密码是否匹配。
- 对于这类应用,Apollo支持应用方经过开放接口在Apollo进行配置的修改和发布,而且具有完善的受权和权限控制
- 部署简单
- 配置中心做为基础服务,可用性要求很是高,这就要求Apollo对外部依赖尽量地少
- 目前惟一的外部依赖是MySQL,因此部署很是简单,只要安装好Java和MySQL就可让Apollo跑起来
- Apollo还提供了打包脚本,一键就能够生成全部须要的安装包,而且支持自定义运行时参数
Apollo 整体设计
官方文档:https://github.com/ctripcorp/apollo/wiki/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.3 各模块概要介绍
1.3.1 Config Service
- 提供配置获取接口
- 提供配置更新推送接口(基于Http long polling)
- 服务端使用Spring DeferredResult实现异步化,从而大大增长长链接数量
- 目前使用的tomcat embed默认配置是最多10000个链接(能够调整),使用了4C8G的虚拟机实测能够支撑10000个链接,因此知足需求(一个应用实例只会发起一个长链接)。
- 接口服务对象为Apollo客户端
1.3.2 Admin Service
- 提供配置管理接口
- 提供配置修改、发布等接口
- 接口服务对象为Portal
1.3.3 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一致
1.3.4 Eureka
- 基于Eureka和Spring Cloud Netflix提供服务注册和发现
- Config Service和Admin Service会向Eureka注册服务,并保持心跳
- 为了简单起见,目前Eureka在部署时和Config Service是在一个JVM进程中的(经过Spring Cloud Netflix)
1.3.5 Portal
- 提供Web界面供用户管理配置
- 经过Meta Server获取Admin Service服务列表(IP+Port),经过IP+Port访问服务
- 在Portal侧作load balance、错误重试
1.3.6 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后,通知对应的客户端
客户端
上图简要描述了Apollo客户端的实现原理:
- 客户端和服务端保持了一个长链接,从而能第一时间得到配置更新的推送。(经过Http Long Polling实现)
- 客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
- 这是一个fallback机制,为了防止推送机制失效致使配置不更新
- 客户端定时拉取会上报本地版本,因此通常状况下,对于定时拉取的操做,服务端都会返回304 - Not Modified
- 定时频率默认为每5分钟拉取一次,客户端也能够经过在运行时指定System Property:
apollo.refreshInterval
来覆盖,单位为分钟。
- 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
- 客户端会把从服务端获取到的配置在本地文件系统缓存一份
- 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
- 应用程序能够从Apollo客户端获取最新的配置、订阅配置更新通知
环境准备
点击连接观看:Apollo 搭建服务端视频(获取更多请关注公众号「哈喽沃德先生」)
Java
- Apollo 服务端:1.8+
- Apollo 客户端:1.7+
因为须要同时启动服务端和客户端,因此建议安装Java 1.8+。
MySQL
- 版本要求:5.6.5+
Apollo的表结构对timestamp
使用了多个default声明,因此须要5.6.5以上版本。
下载Quick Start安装包
Apollo 给咱们准备好了一个Quick Start安装包,你们只须要下载到本地,就能够直接使用,免去了编译、打包过程。
安装包共50M,若是访问github网速不给力的话,能够从百度网盘下载。
- 从Github下载
- checkout或下载apollo-build-scripts项目
- 因为Quick Start项目比较大,因此放在了另外的repository,请注意项目地址
- 从百度网盘下载
- 经过https://github.com/ctripcorp/apollo/wiki/Quick-Start#13-下载quick-start安装包页面的网盘连接下载
- 下载到本地后,在本地解压apollo-quick-start.zip
- 为啥安装包要58M这么大?
- 由于这是一个能够自启动的jar包,里面包含了全部依赖jar包以及一个内置的tomcat容器
安装 Apollo
建立数据库
Apollo 服务端共须要两个数据库:ApolloPortalDB
和ApolloConfigDB
,咱们把数据库、表的建立和样例数据都分别准备了 sql 文件,只须要导入数据库便可。
注意:若是你本地已经建立过Apollo数据库,请注意备份数据。咱们准备的sql文件会清空Apollo相关的表。
建立 ApolloPortalDB 数据库
经过各类MySQL客户端导入sql/apolloportaldb.sql便可。
建立 ApolloConfigDB 数据库
经过各类MySQL客户端导入sql/apolloconfigdb.sql便可。
配置数据库链接信息
Apollo 服务端须要知道如何链接到你前面建立的数据库,因此须要编辑demo.sh,修改 ApolloPortalDB 和 ApolloConfigDB 相关的数据库链接串信息。
注意:填入的用户须要具有对 ApolloPortalDB 和 ApolloConfigDB 数据的读写权限。
#apollo config db info apollo_config_db_url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8 apollo_config_db_username=用户名 apollo_config_db_password=密码(若是没有密码,留空便可) # apollo portal db info apollo_portal_db_url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8 apollo_portal_db_username=用户名 apollo_portal_db_password=密码(若是没有密码,留空便可)
注意:不要修改 demo.sh 的其它部分
搭建服务端
确保端口未被占用
Quick Start脚本会在本地启动3个服务,分别使用8070, 8080, 8090端口,请确保这3个端口当前没有被使用。
执行启动脚本
./demo.sh start
Apollo 提供的脚本文件为 .sh
文件,若是你的安装环境是在 Linux 系统下直接运行以上命令便可,若是你想在 Windows 环境下运行该脚本,先安装 Git 而后在 demo.sh
所在目录下鼠标右键点击 Git Bash Here
,而后再经过以上命令运行脚本便可。
当看到以下输出后,就说明启动成功了!
==== starting service ==== Service logging file is ./service/apollo-service.log Started [10768] Waiting for config service startup....... Config service started. You may visit http://localhost:8080 for service status now! Waiting for admin service startup.... Admin service started ==== starting portal ==== Portal logging file is ./portal/apollo-portal.log Started [10846] Waiting for portal startup...... Portal started. You can visit http://localhost:8070 now!
异常排查
若是启动遇到了异常,能够分别查看 service 和 portal 目录下的 log 文件排查问题。
注:在启动 apollo-configservice 的过程当中会在日志中输出 eureka 注册失败的信息,如
com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused
。须要注意的是,这个是预期的状况,由于 apollo-configservice 须要向 Meta Server(它本身)注册服务,可是由于在启动过程当中,本身还没起来,因此会报这个错。后面会进行重试的动做,因此等本身服务起来后就会注册正常了。
访问
访问:http://localhost:8070/ Quick Start 集成了 Spring Security,输入用户名 apollo,密码 admin 后登陆。
登陆成功后,首页以下,Apollo 还提供了一个 SampleApp
样本案例供咱们学习使用。
建立项目
点击对应按钮建立项目。
这里先经过默认的样例部门演示(后面我会讲如何添加部门),AppId 对应客户端配置文件中 app.id。
建立成功以下图。
客户端接入服务端
点击连接观看:Apollo 客户端接入服务端视频(获取更多请关注公众号「哈喽沃德先生」)
下面经过最经常使用、便捷的方式,即基于 Spring Boot 的集成方式来接入服务端。
apollo-demo
聚合工程。Spring Boot 2.2.4.RELEASE
order-service
:订单服务,端口9090
order-service02
:订单服务,端口9091
添加依赖
<!-- https://mvnrepository.com/artifact/com.ctrip.framework.apollo/apollo-client --> <dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.6.0</version> </dependency>
配置文件
order-service
和 order-service02
的配置信息除端口外一致。
server: port: 9090 # 端口 spring: application: name: order-service # 应用名称 # apollo 相关配置 app: id: order-service # 与 Apollo 配置中心中的 AppId 一致 apollo: meta: http://localhost:8080 # Apollo 中的 Eureka 注册中心地址 #cluster: # 指定 Apollo 集群,相同集群实例使用对应集群的配置 #cacheDir: # 配置缓存目录,网络不可用时任然可提供配置服务 bootstrap: enable: true # 启用 apollo env: DEV # 指定环境 # 自定义配置 name: order-service-dev mysql: host: localhost port: 3306 username: root password: root
配置文件实体类
order-service
和 order-service02
的配置文件实体类代码一致。
package com.example.config; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component @Data @NoArgsConstructor @AllArgsConstructor public class ConfigProperties { @Value("${name}") private String name; @Value("${mysql.host}") private String mysqlHost; @Value("${mysql.port}") private Integer mysqlPort; @Value("${mysql.username}") private String mysqlUsername; @Value("${mysql.password}") private String mysqlPassword; }
控制层
order-service
和 order-service02
的控制层代码一致。
package com.example.controller; import com.example.config.ConfigProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController public class ConfigController { @Autowired private ConfigProperties configProperties; @Value("${name}") private String name; @GetMapping("/name") public String getName() { return configProperties.getName(); } @GetMapping("/mysql") public Map<Object, Object> getMySQLProperties() { // JDK9中的新特性,快速建立只读集合。 return Map.of("host", configProperties.getMysqlHost(), "port", configProperties.getMysqlPort(), "username", configProperties.getMysqlUsername(), "password", configProperties.getMysqlPassword()); } }
启动类
启动类须要添加 @EnableApolloConfig
注解。
order-service
和 order-service02
的启动类代码一致。
package com.example; import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @EnableApolloConfig @SpringBootApplication public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } }
测试
修改配置信息前
访问:http://localhost:9090/name 和 http://localhost:9091/name 结果以下:
访问:http://localhost:9090/mysql 和 http://localhost:9091/mysql 结果以下:
新增配置信息
进入项目后点击右上角的 新增配置
。
添加配置项 name
、mysql.username
、mysql.password
。
发布配置信息
将刚才添加的配置信息批量发布至应用。
修改配置信息后
控制台打印信息以下:
c.f.a.s.p.AutoUpdateConfigChangeListener : Auto update apollo changed value successfully, new value: order-service-dev-2.0, key: name, beanName: configController, field: com.example.controller.ConfigController.name c.f.a.s.p.AutoUpdateConfigChangeListener : Auto update apollo changed value successfully, new value: root123, key: mysql.password, beanName: configProperties, field: com.example.config.ConfigProperties.mysqlPassword c.f.a.s.p.AutoUpdateConfigChangeListener : Auto update apollo changed value successfully, new value: root123, key: mysql.username, beanName: configProperties, field: com.example.config.ConfigProperties.mysqlUsername
访问:http://localhost:9090/name 和 http://localhost:9091/name 结果以下:
访问:http://localhost:9090/mysql 和 http://localhost:9091/mysql 结果以下:
以上只是 Apollo 的入门教程,后面咱们会学习 Apollo 的更多高级玩法,好比多环境部署,高可用环境搭建等等。
下一篇咱们讲解 Apollo 部门管理、用户管理、配置管理、集群管理,记得关注噢~
本文采用 知识共享「署名-非商业性使用-禁止演绎 4.0 国际」许可协议
。
你们能够经过 分类
查看更多关于 Spring Cloud
的文章。
🤗 您的点赞
和转发
是对我最大的支持。
📢 扫码关注 哈喽沃德先生
「文档 + 视频」每篇文章都配有专门视频讲解,学习更轻松噢 ~