分布式配置中心--Apollo

Apollo(阿波罗)是携程开源的分布式配置中心,可以集中化管理应用不一样环境、不一样集群的配置,支持配置热发布并实时推送到应用端,而且具有规范的权限及流程治理等特性,适用于分布式微服务配置管理场景java

Apollo配置中心介绍

程序功能日益复杂,程序配置日益增多:各类功能开关、参数配置、服务器地址...对程序配置的指望也愈来愈高:热部署并实时生效、灰度发布、分环境分集群管理配置、完善的权限审核机制...在这样的背景下,Apollo配置中心应运而生。Apollo支持四个维度Key-Value格式的配置* Application(应用) 实际使用配置的应用,Apollo客户端在运行时须要知道当前应用是谁,从而能够去获取对应的配置。每一个应用都有对应的身份标识--appId,须要在代码中配置数据库

  • Environment(环境) 配置对应的环境,Apollo客户端须要知道当前应用出于哪一个环境,,从而能够去获取应用的配置;环境和代码无关,同一份代码部署在不一样的环境就应该获取不一样环境的配置;环境默认是经过读取机器上的配置(server.properties的env属性)指定的
  • Cluster(集群) 一个应用下不一样实例的分组,例如按照不一样数据中心划分,把上海机房的实例分为一个集群、把深圳机房的实例分为一个集群;对于不一样的Cluster,同一个配置能够有不同的值;集群默认是经过读取机器上的配置指定的(server.properties的idc属性)
  • Namespace(命名空间) 一个应用下不一样配置的分组,是配置项的集合,能够简单地把Namespace类别为(配置)文件,不一样类型的配置存放在不一样的文件中,例如数据库配置文件、RPC配置文件、应用自身的配置文件等;应用能够直接读取到公共组件的配置namespace,例如DAL、RPC等;应用也能够经过继承公共组件的配置namespace来对公共组件的配置作调整,如DAL的初始数据库链接数

Apollo在建立项目的时候,都会默认建立一个"application"的Namespace,"application"是个应用自身使用的。例如Spring Boot中项目的默认配置文件application.yaml,这里application.yaml就等同于"application"的Namespace。对于大多数应用来讲,"application"Namespace已经能知足平常配置使用场景json

客户端获取"application"Namespace的代码以下缓存

Config config = ConfigService.getAppConfig()复制代码

客户端获取非"application"Namespace的代码以下安全

Config config = ConfigService.getConfig(namespaceName)复制代码

Namespace的格式 配置文件有多种格式,properties、xml、yml、yaml、json等,一样Namespace也具备这些格式tips: 非properties格式的namespace,在客户端使用时须要调用ConfigService.getConfigFile(String namespace, ConfigFileFormat configFileFormat)来获取,若是使用Htpp接口直接调用时,对应的namespace参数须要传入namespace的名字加上后缀名,如datasource.jsonNamespace的获取权限分类 此处权限相是对于Apollo客户端来讲的private(私有的)权限 private权限的Namespace,只能被所属的应用获取到。一个应用尝试获取其余应用private的Namespace,Apollo客户端会报"404"异常服务器

public(公共的)权限 具备public权限的Namespace,能被任何应用获取网络

Namespace的类型架构

  • 私有类型 具备private权限,例如上文中提到的"application"Namespace就是私有类型
  • 公共类型 具备public权限,公共类型的Namespace至关于游离于应用以外的配置,且经过Namespace的名称去标识公共Namespace,因此公共Namespace的名称必须全局惟一

使用场景 部门级别共享的配置、小组级别共享的配置、几个项目之间共享的配置、中间件客户端的配置并发

  • 关联类型(继承类型) 具备private权限,关联类型的Namespace继承于公共类型的Namespace,用于覆盖公共Namespace的某些配置。例如公共Namespace有两个配置项
k1 = v1
        k2 = v2复制代码
而后应用A有一个关联类型的Namespace关联此公共Namespace,且以新值v3覆盖配置项k1。那么在应用A实际运行时,获取到的公共Namespace的配置为复制代码
k1 = v3
        k2 = v2复制代码
使用场景  假设RPC框架的配置(如:timeout)有如下要求
*     提供一份全公司默认的配置,且可动态调整
* RPC客户端项目能够自定义某些配置项且可动态调整
        结合Apollo的公共类型的Namespace和关联类型的Namespace。RPC团队在Apollo上维护一个叫“rpc-client”的公共Namespace,在"rpc-client"Namespace上配置默认的参数值。rpc-client.jar里的代码读取"rpc-client"Namespace的配置便可;如须要调整默认的配置,只须要修改公共类型"rpc-client"Namespace的配置;若是客户端项目想要自定义或动态修改某些配置项,只须要在Apollo本身项目下关联"rpc-client",就能建立关联类型"rpc-client"的Namespace,而后在关联类型下修改配置项便可。这里rpc-client.jar是在应用容器里运行的,因此rpc-client获取到"rpc-client"Namespace的配置是应用的关联类型的Namespace加上公共类型的Namespace
        例子  以下图,有三个应用:应用A、应用B、应用C
        应用A有两个私有类型的Namespace:application和NS-Private,以及一个关联类型的Namespace:NS-Public
        应用B有一个私有类型的Namespace:application,以及一个公共类型的Namespace:NS-Public
        应用C只有一个私有类型的Namespace:application
        ![](https://user-gold-cdn.xitu.io/2019/9/29/16d7d726928b404f?w=800&h=460&f=jpeg&s=22615)复制代码
应用A获取Apollo配置
        //application 
        Config appConfig = ConfigService.getAppConfig();
        appConfig.getProperty("k1", null); // k1 = v11
        appConfig.getProperty("k2", null); // k2 = v21
        //NS-Private
        Config privateConfig = ConfigService.getConfig("NS-Private");
        privateConfig.getProperty("k1", null); // k1 = v3
        privateConfig.getProperty("k3", null); // k3 = v4
        //NS-Public,覆盖公共类型配置的状况,k4被覆盖
        Config publicConfig = ConfigService.getConfig("NS-Public");
        publicConfig.getProperty("k4", null); // k4 = v6 cover
        publicConfig.getProperty("k6", null); // k6 = v6
        publicConfig.getProperty("k7", null); // k7 = v7复制代码
应用B获取Apollo配置
        //application
        Config appConfig = ConfigService.getAppConfig();
        appConfig.getProperty("k1", null); // k1 = v12
        appConfig.getProperty("k2", null); // k2 = null
        appConfig.getProperty("k3", null); // k3 = v32复制代码
//NS-Private,因为没有NS-Private Namespace 因此获取到default value
        Config privateConfig = ConfigService.getConfig("NS-Private");
        privateConfig.getProperty("k1", "default value"); 
        //NS-Public
        Config publicConfig = ConfigService.getConfig("NS-Public");
        publicConfig.getProperty("k4", null); // k4 = v5
        publicConfig.getProperty("k6", null); // k6 = v6
        publicConfig.getProperty("k7", null); // k7 = v7复制代码
应用C获取Apollo配置
        //application
        Config appConfig = ConfigService.getAppConfig();
        appConfig.getProperty("k1", null); // k1 = v12
        appConfig.getProperty("k2", null); // k2 = null
        appConfig.getProperty("k3", null); // k3 = v33
        //NS-Private,因为没有NS-Private Namespace 因此获取到default value
        Config privateConfig = ConfigService.getConfig("NS-Private");
        privateConfig.getProperty("k1", "default value"); 
        //NS-Public,公共类型的Namespace,任何项目均可以获取到
        Config publicConfig = ConfigService.getConfig("NS-Public");
        publicConfig.getProperty("k4", null); // k4 = v5
        publicConfig.getProperty("k6", null); // k6 = v6
        publicConfig.getProperty("k7", null); // k7 = v7
        复制代码

ChangeListener 以上代码能够看出,在客户端Namespace映射成一个Config对象,Namespace配置变动的监听器是注册在Config对象上app

在应用A中监听application的Namespace代码以下

Config appConfig = ConfigService.getAppConfig();appConfig.addChangeListener(new ConfigChangeListener() {public void onChange(ConfigChangeEvent changeEvent) {//do something}})

在应用A中监听 NS-Private 的 Namespace代码以下
        Config privateConfig = ConfigService.getConfig("NS-Private");
        privateConfig.addChangeListener(new ConfigChangeListener() {
        public void onChange(ConfigChangeEvent changeEvent) {
        //do something
        }
        })
        ## 在应用A、应用B和应用C中监听NS-Public Namespace代码以下
        Config publicConfig = ConfigService.getConfig("NS-Public");
        publicConfig.addChangeListener(new ConfigChangeListener() {
            public void onChange(ConfigChangeEvent changeEvent) {
                //do something
                }
        })
        复制代码

配置的几大属性

  • 配置是独立于程序的只读变量
    • 配置首先是独立于程序的,同一份程序在不一样的配置下会有不一样的行为
    • 配置对于程序是只读的,程序经过读取配置来改变本身的行为,程序不该该去改变配置
  • 配置伴随应用的整个生命周期
    • 配置贯穿于应用的整个生命周期,应用在启动时经过读取配置来初始化,在运行时根据配置来调整行为
  • 配置能够有多种加载方式
    • 常见的配置加载方式有程序内部hard code、配置文件、环境变量、启动参数、基于数据库等
  • 配置须要治理
    • 权限控制 因为配置能改变程序行为,不正确的配置甚至能引发灾难,因此对配置的修改必须有比较完善的权限控制
    • 不一样环境、集群配置管理 同一份程序在不一样的环境(开发、测试、生产)、不一样的集群(如不一样的数据中心)可能有不一样的配置,因此须要有完善的环境、集群配置管理
    • 框架类组件配置管理 一类比较特殊的配置,一般是由其余团队开发、维护,可是运行时是在业务实际应用内的,因此本质上能够认为框架类组件也是应用的一部分,也须要比较完善的管理方式
      基于配置的特殊性,Apollo从设计之初就立志于成为一个有治理能力的配置发布平台,目前提供了如下的特性
  • 统一管理不一样环境、不一样集群的配置
    • Apollo提供了一个统一界面集中式管理不一样环境(Environment)、不一样集群(Cluster)、不一样命名空间(Namespace)的配置
    • 同一份代码部署在不一样的集群,能够有不一样的配置
    • 经过命名空间能够很方便地支持多个不一样应用共享同一份配置,同时还容许应用对共享的配置进行覆盖
  • 热发布--配置修改实时生效 用户在Apollo修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并通知到应用程序
  • 版本发布管理 全部的配置发布都有版本概念,从而能够方便地支持配置的回滚
  • 灰度发布 支持配置的灰度发布,好比点了发布后,只对部分应用实例生效,等观察一段时间没问题后再推给全部应用实例
  • 权限管理、发布审核、操做审计
    • 应用和配置的管理都有完善的权限管理机制,对配置的管理还分为了编辑和发布两个环节,从而减小人为的错误
    • 全部的操做都有审计日志,能够方便地追踪问题
  • 客户端配置信息监控 能够在界面上方便地看到配置在被哪些实例使用
  • 提供Java和.Net原生客户端
    • 提供了Java和.Net的原生客户端,方便应用集成
    • 支持Spring Placeholder, Annotation和Spring Boot的ConfigurationProperties,方便应用使用(须要Spring 3.1.1+)
    • 同时提供了Http接口,非Java和.Net应用也能够方便地使用
  • 提供开放平台API
  • 部署简单

配置获取规则 仅当应用自定义了集群或namespace才须要。有了cluster概念后,配置的规则就显得重要了,好比应用部署在A机房,可是并无在Apollo新建cluster或者在运行时指定了cluster=SomeCluster,可是并无在Apollo新建cluster,这时候Apollo的行为是怎样的?下面介绍配置获取的规则应用自身配置的获取规则当应用使用下面的语句获取配置时,称之为获取应用自身的配置,也就是应用自身的application namespace的配置

Config config = ConfigService.getAppConfig();复制代码

这种状况的配置获取规则简而言之以下

  1. 首先查找运行时cluster的配置(经过apollo.cluster指定)
  2. 若是没有找到,则查找数据中心cluster的配置
  3. 若是仍是没有找到,则返回默认cluster的配置
    图示以下
    配置查找顺序
因此,若是应用部署在A数据中心,可是用户没有在Apollo建立cluster,那么获取的配置就是默认cluster(default)的;若是应用部署在A数据中心,同时在运行时指定了apollo.cluster=SomeCluster,可是没有在Apollo建立cluster,那么获取的配置就是A数据中心cluster的配置,若是A数据中心cluster没有配置的话,那么获取的配置就是默认cluster(default)的
* 公共组件配置的获取规则
    以`FX.Hermes.Producer`为例,hermes producer是hermes发布的公共组件。当使用下面的语句获取配置时,称之为获取公共组件的配置复制代码
Config config = ConfigService.getConfig("FX.Hermes.Producer")复制代码

对于这种状况获取配置规则,简而言之以下

  1. 首先获取当前应用下的FX.Hermes.Producernamespace的配置
  2. 而后获取hermes应用下FX.Hermes.Producernamespace的配置
  3. 上面两部分配置的并集就是最终使用的配置,若有key同样的部分,应当以应用优先
    图示以下
    公共组件配置获取规则

经过这种方式实现对框架组件的配置管理,框架组件提供方提供配置的默认值,应用若是有特殊需求能够自行覆盖

Apollo配置中心的设计

整体设计

  • 基础模型
    • 用户在配置中心对配置进行修改并发布
    • 配置中心通知Apollo客户端有配置更新
    • Apollo客户端从配置中心拉取最新的配置、更新本地配置并通知到应用

基础模型

  • 架构模块
    • Apollo包含七个模块:四个功能相关的核心模块和三个辅助服务发现的模块
    • 四个核心模块及其主要功能
      • ConfigService 提供配置获取接口、提供配置推送接口、服务于Apollo客户端
      • AdminService 提供配置管理接口、提供配置修改发布接口、服务于管理界面Portal
      • Client 为应用获取配置,支持实时更新、经过MetaServer获取ConfigService的服务列表、使用客户端软负载SLB方式调用ConfigService
      • Portal 配置管理界面、经过MetaServer获取AdminService的服务列表、使用客户端软负载SLB的方式调用AdminService
    • 三个辅助服务发现模块
      • Eureka 用于服务发现和注册、ConfigService和AdminService注册实例并按期上报心跳、和ConfigService部署于同一个进程
      • MetaServer Portal经过域名访问MetaServer获取AdminService的地址列表、Client经过域名访问MetaServer获取ConfigService的地址列表、至关于一个EurekaProxy、是一个逻辑角色和ConfigService部署于同一个进程
      • NginxLB 和域名系统配合,协助Portal访问MetaServer获取AdminService地址列表、和域名系统配合,协助Client访问MetaServer获取ConfigService地址列表、和域名系统配合,协助用户访问Portal进行配置管理
        Apollo架构
  • 架构剖析
**Apollo架构V1**  若是不考虑分布式微服务架构中的服务发现问题,Apollo的最简架构以下图所示
    ![Apollo架构V1](https://user-gold-cdn.xitu.io/2019/9/29/16d7d7275299a778?w=800&h=315&f=jpeg&s=16632)
    要点
    * ConfigService是一个独立的微服务,服务于Client进行配置获取
    * Client和ConfigService保持长链接,经过一种推拉结合(push & pull)的模式,在实现配置实时更新的同时,保证配置更新不丢失
    * AdminService是一个独立的微服务,服务于Portal进行配置管理。Portal经过调用AdminService进行配置管理和发布
    * ConfigService和AdminService共享ConfigDB,ConfigDB中存放项目在某个环境中的配置信息。ConfigService/AdminService/ConfigDB三者在每一个环境(DEV/FAT/UAT/PRO)中都要部署一份
    * Protal有一个独立的PortalDB,存放用户权限、项目和配置的元数据信息。Protal只需部署一份,它能够管理多套环境复制代码
**Apollo架构 V2**   为了保证高可用,ConfigService和AdminService都是无状态以集群方式部署的,这时候就存在一个服务发现的问题:Client怎么找到ConfigService?Portal怎么找到AdminService?为了解决这个问题,Apollo在其架构中引入Eureka服务注册中心组件,实现微服务间的服务注册和发现,更新后的架构以下图所示
        ![Apollo架构V2](https://user-gold-cdn.xitu.io/2019/9/29/16d7d727764634a6?w=800&h=502&f=jpeg&s=23979)
        要点
    * ConfigService和AdminService启动后都会注册到Eureka服务注册中心,并按期发送存活心跳
    * Eureka采用集群方式部署,使用分布式一致性协议保证每一个实例的状态最终一致
复制代码

Apollo架构V3 Eureka是自带服务发现的Java客户端的,若是Apollo只支持Java客户端接入,不支持其它语言客户端接入的话,那么Client和Portal只须要引入Eureka的Java客户端,就能够实现服务发现功能。发现目标服务后,经过客户端软负载(SLB,例如Ribbon)就能够路由到目标服务实例。这是一个经典的微服务架构,基于Eureka实现服务注册发现+客户端Ribbon配合实现软路由,以下图所示Apollo架构V3

Apollo架构V4
为支持多语言客户端接入,Apollo引入MetaServer角色,它实际上是一个Eureka的Proxy,将Eureka的服务发现接口以更简单明确的HTTP接口的形式暴露出来,方便Client/Protal经过简单的HTTPClient就能够查询到ConfigService/AdminService的地址列表。获取到服务实例地址列表以后,再以简单的客户端软负载(Client SLB)策略路由定位到目标实例,并发起调用

另外一个问题,MetaServer自己也是无状态以集群方式部署的,那么Client/Protal该如何发现MetaServer呢?一种传统的作法是借助硬件或者软件负载均衡器,在携程采用的是扩展后的NginxLB(Software Load Balancer),由运维为MetaServer集群配置一个域名,指向NginxLB集群,NginxLB再对MetaServer进行负载均衡和流量转发。Client/Portal经过域名+NginxLB间接访问MetaServer集群

引入MetaServer和NginxLB以后的架构以下图Apollo架构V4

Apollo架构V5
还剩下最后一个环节,Portal也是无状态的以集群方式部署的,用户如何发现和访问Portal?答案也是简单的传统作法,用户经过域名+NginxLB间接访问Portal集群。因此V5版本是包括用户端的最终的Apollo架构全貌,以下图所示Apollo架构V5

  • 服务端设计
配置发布后的实时推送设计  在配置中心中,一个重要的功能就是配置发布后实时推送到客户端。下面咱们简要看一下这块是怎么设计实现的
![服务端设计](https://user-gold-cdn.xitu.io/2019/9/29/16d7d7280f721f5f?w=800&h=280&f=jpeg&s=15776)复制代码
上图简要描述了配置发布的大体过程
        1. 用户在Portal操做发布配置
        2. Portal调用Admin Service的接口操做发布
        3. Admin Service发布配置后,发送ReleaseMessage给各Config Service
        4. Config Service收到ReleaseMessage后通知对应的客户端复制代码
**发送ReleaseMessage的实现方式**   Admin Service在配置发布后,须要通知全部的Config Service有配置发布,从而Config Service能够通知对应的客户端来拉取最新的配置。从概念上看,这是一个典型的消息使用场景,Admin Service做为Producer发出消息,各个Config Service做为consumer消费消息。经过一个消息组件(Message Queue)就能很好地实现Admin Service和Config Service的解耦。在实现上,Apollo为尽可能减小外部依赖,没有采用外部的消息中间件,而是经过数据库实现了一个简单的消息队列复制代码
实现方式以下
        1. Admin Service在配置发布后会往ReleaseMessage表插入一条消息记录,消息内容就是配置发布的AppId+Cluster+Namespace
        2. Config Service有一个线程会每秒扫描一次ReleaseMessage表,看是否有新的消息记
        3. Config Service若是发现有新的消息记录,那么会通知到全部的消息监听器(ReleaseMessageListener),例如NotificationControllerV2
        4. 消息监听器获得配置发布的AppId+Cluster+Namespace后,会通知对应的客户端
示意图以下
![配置更新通知](https://user-gold-cdn.xitu.io/2019/9/29/16d7d728386cd0b0?w=800&h=788&f=jpeg&s=29770)复制代码

Config Service通知客户端的实现方式 消息监听器在得知有新的配置发布后是如何通知到客户端的呢?其实现方式以下

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

客户端设计客户端设计上图简要描述了Apollo客户端的实现原理

  1. 客户端和服务端保持了一个长链接(经过Http Long Polling实现),从而能第一时间得到配置更新的推送
  2. 客户端还会按期从Apollo配置中心服务端拉取应用的最新配置
    • 这是一个fallback机制,为了防止推送机制失效致使配置不更新
    • 客户端定时拉取会上报本地版本,全部通常状况下,对于定时拉取的操做,服务端都会返回304-Not Modified
    • 定时频率默认为每5分钟拉取一次,客户端也能够经过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟
    • 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
    • 客户端会把从服务端获取到的配置在本地文件系统缓存一份,在遇到服务不能够或网络不通时,依然能从本地恢复配置
    • 应用程序能够从Apollo客户端获取最新的配置、订阅配置更新通知

Apollo使用指南

名称解析

  • 普通应用 独立运行的程序,如Web应用程序、带有main函数的程序
  • 公共组件 指发布的类库、客户端程序,不会本身独立运行,如Java的jar包

普通应用接入指南

  • 建立项目
    • 1.进入apollo-portal主页
    • 2.点击"建立项目"
    • 3.输入项目信息
      部门 选择应用所在的部门
      应用AppId 用来表示应用身份的惟一id,格式为string,须要和客户端app.properties中配置的app.id对应
      应用名称 应用名,仅用于界面展现
      应用负责人 选择的人默认会成为该项目的管理员,具有项目权限管理、集群建立、Namespace建立等权限
    • 4.点击提交 建立成功后默认会跳转到,项目首页
  • 项目权限分配
    项目管理员权限 项目管理员能够管理项目的权限分配、能够建立集群、能够建立Namespace
  • 配置编辑发布权限
    编辑权限容许用户在Apollo界面上建立、修改、删除配置
    发布权限容许用户在Apollo界面上发布、回滚配置
  • 添加配置项 编辑配置须要拥有这个Namespace的编辑权限,若是发现没有新增配置按钮,能够找项目管理员受权
  • 发布配置 配置只有在发布后才会真的被应用使用到,因此在编辑完配置后,须要发布配置。发布配置须要拥有这个Namespace的发布权限,若是发现没有发布按钮,能够找项目管理员受权
  • 应用读取配置 配置发布成功后就能够经过Apollo客户端读取到配置了。Apollo目前提供Java客户端,若是使用其余语言,也能够经过直接访问Http接口获取配置
  • 回滚已发布配置 若是发现已发布的配置有问题,能够经过点击"回滚"按钮来将客户端读取到的配置回滚到上一个发布版本。这里的回滚机制相似于发布系统,发布系统中的回滚操做是将部署到机器上的安装包回滚到上一个部署的版本,但代码仓库中的代码是不会回滚的,从而开发能够在修复代码后从新发布。Apollo中的回滚也是相似的机制,点击回滚后是将发布到客户端的配置回滚到上一个已发布版本,也就是说客户端读取到的配置会恢复到上一个版本,但页面上编辑状态的配置是不会回滚的,从而开发能够在修复配置后从新发布

公共组件接入步骤公共组件接入步骤几乎与普通应用接入一致,惟一的区别是公共组件须要创建本身的惟一Namespace

1.建立项目
        2.项目管理员权限
        3.建立Namespace
        4.添加配置项
        5.发布配置
        6.应用读取配置复制代码

应用覆盖公共组件配置步骤

1.关联公共组件Namespace
    2.覆盖公共组件配置
    3.发布配置复制代码

多个AppId共享同一份配置在一些状况下,尽管应用自己不是公共组件,但仍是须要在多个AppId之间共用同一份配置,这种状况下若是但愿实现多个AppId使用同一份配置的话,基本概念和公共组件的配置是一致的。具体来讲,就是在其中一个AppId下建立一个namespace,写入公共的配置信息,而后在各个项目中读取该namespace的配置便可;若是某个AppId须要覆盖公共的配置信息,那么在该AppId下关联公共的namespace并写入须要覆盖的配置便可

应用接入策略这里考虑非Java语言客户端接入--直接经过Http接口获取配置

  • 经过带缓存的HTTP接口从Apollo读取配置
    该接口会从缓存中获取配置,适合频率较高的配置拉取请求,如简单的30秒轮询一次配置。因为缓存最多会有一秒的延迟,因此若是须要配合配置推送通知实现实时更新配置的话,请参考不带缓存的HTTP接口从Apollo读取配置
**HTTP接口说明**复制代码
**URL** {config_server_url}/configfiles/json/{appId}/{clusterName}/{namespaceName}?ip={clientIp}复制代码
**Method**  GET复制代码
**参数说明 ** 
![file](https://user-gold-cdn.xitu.io/2019/9/29/16d7d728a24bb557?w=800&h=682&f=jpeg&s=68943)复制代码
**HTTP接口返回格式**  该HTTP接口返回的是JSON格式、UTF-8编码,包含了对应namespace中全部的配置项。返回内容Sample以下
        {
        "portal.elastic.document.type":"biz",
        "portal.elastic.cluster.name":"hermes-es-fws"
        }
        *TIPS 经过{configserverurl}/configfiles/{appId}/{clusterName}/{namespaceName}?ip={clientIp}能够获取到properties形式的配置*
* 不带缓存的HTTP接口从Apollo读取配置  
    该接口会直接从数据库中获取配置,能够配合配置推送通知实现实时更新配置复制代码
**URL**  {config_server_url}/configs/{appId}/{clusterName}/{namespaceName}?releaseKey={releaseKey}&ip={clientIp}复制代码
**Method** GET复制代码
**参数说明**
![file](https://user-gold-cdn.xitu.io/2019/9/29/16d7d728d1fafe5a?w=800&h=753&f=jpeg&s=77670)复制代码

该HTTP接口返回的是JSON格式、UTF-8编码。若是配置没有变化(传入的releaseKey和服务端的相等),则返回HttpStatus 304,Response Body为空;若是配置有变化,则会返回HttpStatus 200,Response Body为对应namespace的meta信息以及其中全部的配置项。返回内容Sample以下

{
            "appId": "100004458",
            "cluster": "default",
            "namespaceName": "application",
            "configurations": {
            "portal.elastic.document.type":"biz",
            "portal.elastic.cluster.name":"hermes-es-fws"
            },
            "releaseKey": "20170430092936-dee2d58e74515ff3"
            }
            复制代码
  • 应用感知配置更新
    Apollo提供了基于Http long polling的配置更新推送通知,第三方客户端能够看本身实际的需求决定是否须要使用这个功能。若是对配置更新时间不是那么敏感的话,能够经过定时刷新来感知配置更新,刷新频率能够视应用自身状况来定,建议在30秒以上。若是须要作到实时感知配置更新(1秒)的话,能够参考下面的文档实现配置更新推送的功能
**配置更新推送实现思路**  建议参考Apollo的Java实现RemoteConfigLongPollService.java复制代码

初始化 首先须要肯定哪些namespace须要配置更新推送,Apollo的实现方式是程序第一次获取某个namespace的配置时就会来注册一下,咱们就知道有哪些namespace须要配置更新推送了。初始化后的结果就是获得一个notifications的Map,内容是namespaceName -> notificationId(初始值为-1)。运行过程当中若是发现有新的namespace须要配置更新推送,直接塞到notifications这个Map里面便可

请求服务 有了notifications这个Map以后,就能够请求服务了。这里先描述一下请求服务的逻辑,具体的URL参数和说明请参见后面的接口说明

1.请求远端服务,带上本身的应用信息以及notifications信息复制代码
2.服务端针对传过来的每个namespace和对应的notificationId,检查notificationId是不是最新的复制代码
3.若是都是最新的,则保持住请求60秒,若是60秒内没有配置变化,则返回HttpStatus 304。若是60秒内有配置变化,则返回对应namespace的最新notificationId, HttpStatus 200复制代码
4.若是传过来的notifications信息中发现有notificationId比服务端老,则直接返回对应namespace的最新notificationId, HttpStatus 200复制代码
5.客户端拿到服务端返回后,判断返回的HttpStatus复制代码
6.若是返回的HttpStatus是304,说明配置没有变化,从新执行第1步复制代码
7.若是返回的HttpStauts是200,说明配置有变化,针对变化的namespace从新去服务端拉取配置,参见1.3 经过不带缓存的Http接口从Apollo读取配置。同时更新notifications map中的notificationId。从新执行第1步复制代码
HTTP接口说明复制代码
URL {config_server_url}/notifications/v2?appId={appId}&cluster={clusterName}&notifications={notifications}复制代码
Method GET复制代码
参数说明复制代码

file

TIPS 因为服务端会hold住60秒,因此请确保客户端访问服务端的超时时间要大于60秒;记得对参数进行URL Encode

HTTP返回格式  该Http接口返回的是JSON格式、UTF-8编码,包含了有变化的namespace和最新的notificationId。返回内容Sample以下
        [{
            "namespaceName": "application",
            "notificationId": 101
        }]复制代码

分布式部署指南

官方展现的部署策略,生产环境部署一套Apollo-Portal+ApolloPortalDB,其余环境(PRO、UAT、FAT、DEV)单独部署MetaServer+AdminService+ConfigService,使用独立数据库ApolloConfigDB及应用服务;MetaServer和Config Service部署在同一个JVM进程内,Admin Service部署在同一台服务器的另外一个JVM进程内。部署示例以下图file网络策略 分布式部署的时候,apollo-configservice和apollo-adminservice须要把本身的IP和端口注册到Meta Server(apollo-configservice自己)。Apollo客户端和Portal会从Meta Server获取服务的地址(IP+PORT),而后经过服务地址直接访问。apollo-configservice和apollo-adminservice是基于内网可信网络设计的,因此出于安全考虑,请不要将apollo-configservice和apollo-adminservice直接暴露在公网

部署步骤
        建立数据库  Apollo服务端依赖于MYSQL数据库,因此须要事先建立并完成初始化
        获取安装包  Apollo服务端安装包共3个: Apollo-AdminService、Apollo-ConfigService、Apollo-Portal
        部署Apollo服务端  获取安装包后就能够部署到测试和生产环境复制代码

小结

文章较为全面介绍开源分布式配置中心Apollo的设计、使用、应用接入及部署方法,目前客户端只有Java和.Net版本,其余语言客户端的接入能够经过HTTP接口的方式定时拉取更新配置或经过Http Long Polling机制实时推送,实现应用感知配置更新

相关文章
相关标签/搜索