【中间件系列】分布式配置中心Apollo知识点小结

713 阅读9分钟

一、简介

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

服务端基于Spring Boot和Spring Cloud开发,打包后可以直接运行,不需要额外安装Tomcat等应用容器。

Java客户端不依赖任何框架,能够运行于所有Java运行时环境,同时对Spring/Spring Boot环境也有较好的支持

官方github:https://github.com/ctripcorp/apollo

Apollo Wiki: https://www.apolloconfig.com/ 

体验地址:
http://106.54.227.205
User/Password: apollo/admin

二、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还提供了打包脚本,一键就可以生成所有需要的安装包,并且支持自定义运行时参数

三、基础架构图

3.1 下图为我理解的Apollo 架构图

3.2 下图为Apollo 作者宋顺提供的架构图

  • 四个核心模块及其主要功能

    ConfigService

    • 提供配置获取接口
    • 提供配置推送接口
    • 服务于Apollo客户端

    AdminService

    • 提供配置管理接口

    • 提供配置修改发布接口

    • 服务于管理界面Portal

    Client

    • 为应用获取配置,支持实时更新
    • 通过MetaServer获取ConfigService的服务列表
    • 使用客户端软负载SLB方式调用ConfigService

    Portal

    • 配置管理界面
    • 通过MetaServer获取AdminService的服务列表
    • 使用客户端软负载SLB方式调用AdminService
  • 三个辅助服务发现模块 Eureka

    • 用于服务发现和注册

    • Config/AdminService注册实例并定期报心跳

    • 和ConfigService住在一起部署

    MetaServer

    • Portal通过域名访问MetaServer获取AdminService的地址列表

    • Client通过域名访问MetaServer获取ConfigService的地址列表

    • 相当于一个Eureka Proxy

    • 逻辑角色,和ConfigService住在一起部署

    NginxLB

    • 和域名系统配合,协助Portal访问MetaServer获取AdminService地址列表

    • 和域名系统配合,协助Client访问MetaServer获取ConfigService地址列表

    • 和域名系统配合,协助用户访问Portal进行配置管理

参考波波老师的架构分析:

​ 1. 在不考虑 服务发现问题的情况下 ,apollo架构如下:

  1. 为了保证高可用,ConfigService和AdminService都是无状态以集群方式部署的,Apollo在其架构中引入了Eureka服务注册中心组件,实现微服务间的服务注册和发现

  1. 如果服务仅仅是java客户端,可基于Eureka实现服务注册发现+客户端Ribbon配合实现软路由
  2. 如果引入其他客户端,如.NET,Eureka(包括Ribbon软负载) 仅支持java,Apollo的作者引入了MetaServer这个角色,它其实是一个Eureka的Proxy,将Eureka的服务发现接口以更简单明确的HTTP接口的形式暴露出来,方便Client/Protal通过简单的HTTPClient就可以查询到Config/AdminService的地址列表。获取到服务实例地址列表之后,再以简单的客户端软负载(Client SLB)策略路由定位到目标实例,并发起调用。MetaServer也是无状态的,怎么发现MetaServer? 由运维为MetaServer集群配置一个域名,指向NginxLB集群,NginxLB再对MetaServer进行负载均衡和流量转发。Client/Portal通过域名+NginxLB间接访问MetaServer集群。 引入MetaServer和NginxLB之后的架构如下图所示: 5. Portal也是无状态以集群方式部署的,用户如何发现和访问Portal?答案也是简单的传统做法,用户通过域名+NginxLB间接访问Portal集群。

四、 核心模块设计

  • 配置更新如何做到实时通知客户端?(推模式)

  1. 用户在Portal操作配置发布
  2. Portal调用Admin Service的接口操作发布
  3. Admin Service发布配置后,发送ReleaseMessage给各个Config Service
  4. Config Service收到ReleaseMessage后,通知对应的客户端

配置变更消息发送

这是一个典型的消息使用场景,Admin Service作为producer发出消息,各个Config Service作为consumer消费消息。通过一个消息组件(Message Queue)就能很好的实现Admin Service和Config Service的解耦。

在实现上,考虑到Apollo的实际使用场景,以及为了尽可能减少外部依赖,我们没有采用外部的消息中间件,而是通过数据库实现了一个简单的消息队列。(面试题:如何不通过消息中间件,实现一个消息队列?)

实现方式如下:

  1. Admin Service在配置发布后会往ReleaseMessage表插入一条消息记录,消息内容就是配置发布的AppId+Cluster+Namespace,参见DatabaseMessageSender
  2. Config Service有一个线程会每秒扫描一次ReleaseMessage表,看看是否有新的消息记录,参见ReleaseMessageScanner
  3. Config Service如果发现有新的消息记录,那么就会通知到所有的消息监听器(ReleaseMessageListener),如NotificationControllerV2,消息监听器的注册过程参见ConfigServiceAutoConfiguration
  4. NotificationControllerV2得到配置发布的AppId+Cluster+Namespace后,会通知对应的客户端

配置变更消息消费

  1. 客户端会发起一个Http请求到Config Service的notifications/v2接口,也就是NotificationControllerV2,参见RemoteConfigLongPollService
  2. NotificationControllerV2不会立即返回结果,而是通过Spring DeferredResult把请求挂起
  3. 如果在60秒内没有该客户端关心的配置发布,那么会返回Http状态码304给客户端
  4. 如果有该客户端关心的配置发布,NotificationControllerV2会调用DeferredResult的setResult方法,传入有配置变化的namespace信息,同时该请求会立即返回。客户端从返回的结果中获取到配置变化的namespace后,会立即请求Config Service获取该namespace的最新配置。

简言之,client 与configService 保持长链接,有消息变更时,用DeferredResult的setResult方法,设置200,客户端主动发起查询一次配置信息 到内存,实现配置感知。理论上误差时间为1s。

无消息变更时,超时60s后设置返回 304,则不去查询

  • 实时通知失效怎么办?(拉模式轮询)

    每5分钟(轮询时间可调)客户端会轮询一次配置信息,防止消息实时通知失败

  • 所有Config Service下线怎么办?(本地缓存)

​ 可读取本地缓存文件

五、 高可用设计

场景影响降级原因
某台Config Service下线无影响Config Service无状态,客户端重连其它Config Service
所有Config Service下线客户端无法读取最新配置,Portal无影响客户端重启时,可以读取本地缓存配置文件。如果是新扩容的机器,可以从其它机器上获取已缓存的配置文件,具体信息可以参考Java客户端使用指南 - 1.2.3 本地缓存路径
某台Admin Service下线无影响Admin Service无状态,Portal重连其它Admin Service
所有Admin Service下线客户端无影响,Portal无法更新配置
某台Portal下线无影响Portal域名通过SLB绑定多台服务器,重试后指向可用的服务器
全部Portal下线客户端无影响,Portal无法更新配置
某个数据中心下线无影响多数据中心部署,数据完全同步,Meta Server/Portal域名通过SLB自动切换到其它存活的数据中心
数据库宕机客户端无影响,Portal无法更新配置Config Service开启配置缓存后,对配置的读取不受数据库宕机影响

六、 部署

快速开始 www.apolloconfig.com/#/zh/deploy…

七、参考