一、前言
最近我司进行基础架构升级,将配置中心从 Spring Cloud Config 迁移至 Apollo。趁此机会也学习下 Apollo,本文主要知识来自于我对官方 Wiki 的学习,如有错误,欢迎勘误。

Apollo(阿波罗)来自于携程研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
Apollo 服务端 基于 Spring Boot 和 Spring Cloud 开发,因此对于 Spring Cloud 项目能够很好的结合。官方提供 Java 和 .NET 两种语言的不依赖任何框架的客户端,另外还提供了 API 接口,便于其他语言或整合到自有框架中使用。由此看来,Apollo 接入到项目中是较为容易的。
演示环境:106.12.25.204:8070
账号/密码:apollo/admin
二、主要特点
2.1 Feature
(1)统一管理不同环境、不同集群的配置
- Apollo提供了一个统一界面集中式管理不同环境(environment)、不同集群(cluster)、不同命名空间(namespace)的配置。
- 同一份代码部署在不同的集群,可以有不同的配置。
- 通过命名空间(namespace)可以很方便的支持多个不同应用共享同一份配置,同时还允许应用对共享的配置进行覆盖。
(2)配置修改实时生效
用户在 Apollo 修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并加载到应用程序。
(3)版本发布管理
所有的配置发布都有版本概念,从而可以方便的支持配置的回滚。
(4)灰度发布
支持配置的灰度发布,比如点了发布后,只对部分应用实例生效,等观察一段时间没问题后再推给所有应用实例。
(5)权限管理、发布审核、操作审计
-
应用和配置的管理都有完善的权限管理机制,对配置的管理还分为了编辑和发布两个环节,从而减少人为的错误。
-
所有的操作都有审计日志,可以方便的追踪问题。
(6)客户端配置信息监控
- 可以方便的看到配置在被哪些实例使用。
(7)提供 Java 和 .Net 原生客户端
- 提供了 Java 和 .Net 的原生客户端,方便应用集成。
- 支持 Spring Placeholder,Annotation 和 Spring Boot 的 ConfigurationProperties,方便应用使用(需要Spring 3.1.1+)
- 同时提供了 HTTP 接口,非 Java 和 .Net 应用也可以方便的使用
(8)提供开放平台 API
- Apollo 自身提供了比较完善的统一配置管理界面,支持多环境、多数据中心配置管理、权限、流程治理等特性。
- 不过 Apollo 出于通用性考虑,对配置的修改不会做过多限制,只要符合基本的格式就能够保存。
- 在我们的调研中发现,对于有些使用方,它们的配置可能会有比较复杂的格式,如 xml, json,需要对格式做校验。
- 还有一些使用方如 DAL,不仅有特定的格式,而且对输入的值也需要进行校验后方可保存,如检查数据库、用户名和密码是否匹配。
- 对于这类应用,Apollo支持应用方通过开放接口在 Apollo 进行配置的修改和发布,并且具备完善的授权和权限控制。
(9)部署简单
- 配置中心作为基础服务,可用性要求非常高,这就要求 Apollo 对外部依赖尽可能地少
- 目前唯一的外部依赖是 MySQL,所以部署非常简单,只要安装好 Java 和 MySQL 就可以让 Apollo 跑起来
- Apollo 还提供了打包脚本,一键就可以生成所有需要的安装包,并且支持自定义运行时参数
2.2 能做哪些事
2.2.1 开关
(1)发布开关
发布开关一般用于发布过程中,比如:
- 有些新功能依赖于其它系统的新接口,而其它系统的发布周期未必和自己的系统一致,可以加个发布开关,默认把该功能关闭,等依赖系统上线后再打开。
- 有些新功能有较大风险,可以加个发布开关,上线后一旦有问题可以迅速关闭。
需要注意的是,发布开关应该是短暂存在的(1-2 周),一旦功能稳定后需要及时清除开关代码。
(2)实验开关
- 针对特定用户应用新的推荐算法。
- 针对特定百分比的用户使用新的下单流程。
- 有些重大功能已经对外宣称在某年某日发布,可以事先发到生产环境,只对内部用户打开,测试没问题后按时对全部用户开放。
实验开关应该也是短暂存在的,一旦实验结束了需要及时清除实验开关代码。
(3)运维开关
运维开关通常用于提升系统稳定性,比如:
-
大促前可以把一些非关键功能关闭来提升系统容量;
-
当系统出现问题时可以关闭非关键功能来保证核心功能正常工作。
运维开关可能会长期存在,而且一般会涉及多个系统,所以需要提前规划。
2.2.2 服务治理
(1)限流
服务就像高速公路一样,在正常情况下非常通畅,不过一旦流量突增(比如大促、遭受 DDOS 攻击)时,如果没有做好限流,就会导致系统整个被冲垮,所有用户都无法访问。
所以我们需要限流机制来应对此类问题,一般的做法是在网关或 RPC 框架层添加限流逻辑,结合配置中心的动态推送能力实现动态调整限流规则配置。
(2)黑白名单
对于一些关键服务,哪怕是在内网环境中一般也会对调用方有所限制,比如:
-
有敏感信息的服务可以通过配置白名单来限制只有某些应用或 IP 才能调用
-
某个调用方代码有问题导致超大量调用,对服务稳定性产生了影响,可以通过配置黑名单来暂时屏蔽这个调用方或 IP
一般的做法是在 RPC 框架层添加校验逻辑,结合配置中心的动态推送能力来实现动态调整黑白名单配置。
2.2.3 数据库迁移
数据库的迁移也是挺普遍的,比如:原来使用的 SQL Server,现在需要迁移到 MySQL,这种情况就可以结合配置中心来实现平滑迁移:
- 单写 SQL Server,100% 读 SQL Server;
- 初始化 MySQL;
- 双写 SQL Server 和 MySQL,100% 读 SQL Server;
- 线下校验、补齐 MySQL 数据;
- 双写 SQL Server 和 MySQL,90% 读 SQL Server,10% 读 MySQL;
- 双写 SQL Server 和 MySQL,100% 读 MySQL;
- 单写 MySQL,100% 读 MySQL;
- 切换完成。
上述的读写开关和比例配置都可以通过配置中心实现动态调整。

2.2.4 动态日志级别
服务运行过程中,经常会遇到需要通过日志来排查定位问题的情况,然而这里却有个两难:
-
如果日志级别很高(如:ERROR),可能对排查问题也不会有太大帮助
-
如果日志级别很低(如:DEBUG),日常运行会带来非常大的日志量,造成系统性能下降
为了兼顾性能和排查问题,我们可以借助于日志组件和配置中心实现日志级别动态调整。
https://github.com/ctripcorp/apollo-use-cases/tree/master/spring-cloud-logger
2.2.5 动态数据源
数据库是应用运行过程中的一个非常重要的资源,承担了非常重要的角色。
在运行过程中,我们会遇到各种不同的场景需要让应用程序切换数据库连接,比如:数据库维护、数据库宕机主从切换等。
https://github.com/ctripcorp/apollo-use-cases/tree/master/dynamic-datasource
2.3 公共组件的配置
公共组件是指那些发布给其它应用使用的客户端代码,比如 RPC 客户端、DAL 客户端等。
这类组件一般是由单独的团队(如中间件团队)开发、维护,但是运行时是在业务实际应用内的,所以本质上可以认为是应用的一部分。
这类组件的特殊之处在于大部分的应用都会直接使用中间件团队提供的默认值,少部分的应用需要根据自己的实际情况对默认值进行调整。
比如数据库连接池的最小空闲连接数量(minimumIdle),出于对数据库资源的保护,DBA 要求将全公司默认的 minimumIdle 设为 1,对大部分的应用可能都适用,不过有些核心 / 高流量应用可能觉得太小,需要设为 10。
针对这种情况,可以借助于 Apollo 提供的 Namespace 关联类型实现:
- 中间件团队创建一个名为
dal
的公共 Namespace,设置全公司的数据库连接池默认配置 - dal 组件的代码会读取
dal
公共 Namespace 的配置 - 对大部分的应用由于默认配置已经适用,所以不用做任何事情
- 对于少量核心 / 高流量应用如果需要调整 minimumIdle 的值,只需要关联
dal
公共 Namespace,然后对需要覆盖的配置做调整即可,调整后的配置仅对该应用自己生效
<center>
通过这种方式的好处是不管是中间件团队,还是应用开发,都可以灵活地动态调整公共组件的配置。
2.4 灰度发布
对于重要的配置一定要做灰度发布,先在一台或多台机器上生效后观察效果,如果没有问题再推给所有的机器。
对于公共组件的配置,建议先在一个或多个应用上生效后观察效果,没有问题再推给所有的应用。

2.5 发布审核
生产环境建议启用发布审核功能,简单而言就是如果某个人修改了配置,那么必须由另一个人审核后才可以发布,以避免由于头脑不清醒、手一抖之类的造成生产事故。
三、基本介绍
3.1 配置
既然 Apollo 是一款分布式配置中心,首先我们就得搞清楚什么是配置。配置通俗来说就是我们 Java 程序中的 .properties 文件或者是 .yaml 文件,配置一般有以下几个属性:
- 配置是独立于程序的只读变量
- 配置首先是独立于程序的,同一份程序在不同的配置下会有不同的行为。
- 其次,配置对于程序是只读的,程序通过读取配置来改变自己的行为,但是程序不应该去改变配置。
- 常见的配置有:数据库连接信息、线程池大小、缓冲区大小、服务器地址等。
- 配置伴随应用的整个生命周期
- 配置贯穿于应用的整个生命周期,应用在启动时通过读取配置来初始化,在运行时根据配置调整行为。
- 配置可以有多种加载方式
- 配置也有很多种加载方式,常见的有程序内部hard code,配置文件,环境变量,启动参数,基于数据库等
- 配置需要治理
- 权限控制
- 由于配置能改变程序的行为,不正确的配置甚至能引起灾难,所以对配置的修改必须有比较完善的权限控制
- 不同环境、集群配置管理
- 同一份程序在不同的环境(开发,测试,生产)、不同的集群(如不同的数据中心)经常需要有不同的配置,所以需要有完善的环境、集群配置管理
- 框架类组件配置管理
- 还有一类比较特殊的配置 - 框架类组件配置,比如CAT客户端的配置。
- 虽然这类框架类组件是由其他团队开发、维护,但是运行时是在业务实际应用内的,所以本质上可以认为框架类组件也是应用的一部分。
- 这类组件对应的配置也需要有比较完善的管理方式。
- 权限控制