分布式配置中心--Apollo


分布式配置中心--Apollo

分布式配置中心--Apollo

Apollo(阿波羅)是攜程開源的分布式配置中心,能夠集中化管理應用不同環境、不同集群的配置,支持配置熱發布並實時推送到應用端,並且具備規范的權限及流程治理等特性,適用於分布式微服務配置管理場景

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已經能滿足日常配置使用場景客戶端獲取"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.json

Namespace的獲取權限分類 此處權限相是對於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

應用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對象上

## 在應用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.Producer`namespace的配置
  2. 然后獲取hermes應用下`FX.Hermes.Producer`namespace的配置
  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架構V1 如果不考慮分布式微服務架構中的服務發現問題,Apollo的最簡架構如下圖所示

要點

  1. ConfigService是一個獨立的微服務,服務於Client進行配置獲取
  2. Client和ConfigService保持長連接,通過一種推拉結合(push & pull)的模式,在實現配置實時更新的同時,保證配置更新不丟失
  3. AdminService是一個獨立的微服務,服務於Portal進行配置管理。Portal通過調用AdminService進行配置管理和發布
  4. ConfigService和AdminService共享ConfigDB,ConfigDB中存放項目在某個環境中的配置信息。ConfigService/AdminService/ConfigDB三者在每個環境(DEV/FAT/UAT/PRO)中都要部署一份
  5. Protal有一個獨立的PortalDB,存放用戶權限、項目和配置的元數據信息。Protal只需部署一份,它可以管理多套環境

Apollo架構 V2 為了保證高可用,ConfigService和AdminService都是無狀態以集群方式部署的,這時候就存在一個服務發現的問題:Client怎么找到ConfigService?Portal怎么找到AdminService?為了解決這個問題,Apollo在其架構中引入Eureka服務注冊中心組件,實現微服務間的服務注冊和發現,更新后的架構如下圖所示

要點

  1. ConfigService和AdminService啟動后都會注冊到Eureka服務注冊中心,並定期發送存活心跳
  2. Eureka采用集群方式部署,使用分布式一致性協議保證每個實例的狀態最終一致

Apollo架構V3 Eureka是自帶服務發現的Java客戶端的,如果Apollo只支持Java客戶端接入,不支持其它語言客戶端接入的話,那么Client和Portal只需要引入Eureka的Java客戶端,就可以實現服務發現功能。發現目標服務后,通過客戶端軟負載(SLB,例如Ribbon)就可以路由到目標服務實例。這是一個經典的微服務架構,基於Eureka實現服務注冊發現+客戶端Ribbon配合實現軟路由,如下圖所示

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架構V5 還剩下最后一個環節,Portal也是無狀態的以集群方式部署的,用戶如何發現和訪問Portal?答案也是簡單的傳統做法,用戶通過域名+NginxLB間接訪問Portal集群。所以V5版本是包括用戶端的最終的Apollo架構全貌,如下圖所示

服務端設計

配置發布后的實時推送設計 在配置中心中,一個重要的功能就是配置發布后實時推送到客戶端。下面我們簡要看一下這塊是怎么設計實現的

上圖簡要描述了配置發布的大致過程

  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后,會通知對應的客戶端

示意圖如下

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

參數說明

HTTP接口返回格式該HTTP接口返回的是JSON格式、UTF-8編碼,包含了對應namespace中所有的配置項。返回內容Sample如下

{
    "portal.elastic.document.type":"biz",
    "portal.elastic.cluster.name":"hermes-es-fws"
}

TIPS 通過{config_server_url}/configfiles/{appId}/{clusterName}/{namespaceName}?ip={clientIp}可以獲取到properties形式的配置

  • 不帶緩存的HTTP接口從Apollo讀取配置

該接口會直接從數據庫中獲取配置,可以配合配置推送通知實現實時更新配置

URL {config_server_url}/configs/{appId}/{clusterName}/{namespaceName}?releaseKey={releaseKey}&ip={clientIp}

Method GET

參數說明

該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

參數說明

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進程內。部署示例如下圖

 

網絡策略 分布式部署的時候,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機制實時推送,實現應用感知配置更新


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM