Apollo配置中心介紹


概念

隨着程序功能的日益復雜,程序的配置日益增多:各種功能的開關、參數的配置、服務器的地址……對程序配置的期望值也越來越高:配置修改后實時生效,灰度發布,分環境、分集群管理配置,完善的權限、審核機制……在這樣的大環境下,傳統的通過配置文件、數據庫等方式已經越來越無法滿足開發人員對配置管理的需求。

比較

圖片

基本概念

既然Apollo定位於配置中心,那么在這里有必要先簡單介紹一下什么是配置。

按照我們的理解,配置有以下幾個屬性:

  • 配置是獨立於程序的只讀變量
    • 配置首先是獨立於程序的,同一份程序在不同的配置下會有不同的行為。
    • 其次,配置對於程序是只讀的,程序通過讀取配置來改變自己的行為,但是程序不應該去改變配置。
    • 常見的配置有:DB Connection Str、Thread Pool Size、Buffer Size、Request Timeout、Feature Switch、Server Urls等。
  • 配置伴隨應用的整個生命周期
    • 配置貫穿於應用的整個生命周期,應用在啟動時通過讀取配置來初始化,在運行時根據配置調整行為。
  • 配置可以有多種加載方式
    • 配置也有很多種加載方式,常見的有程序內部hard code,配置文件,環境變量,啟動參數,基於數據庫等
  • 配置需要治理
    • 權限控制
      • 由於配置能改變程序的行為,不正確的配置甚至能引起災難,所以對配置的修改必須有比較完善的權限控制
    • 不同環境、集群配置管理
      • 同一份程序在不同的環境(開發,測試,生產)、不同的集群(如不同的數據中心)經常需要有不同的配置,所以需要有完善的環境、集群配置管理
    • 框架類組件配置管理
      • 還有一類比較特殊的配置 - 框架類組件配置,比如CAT客戶端的配置。
      • 雖然這類框架類組件是由其他團隊開發、維護,但是運行時是在業務實際應用內的,所以本質上可以認為框架類組件也是應用的一部分。
      • 這類組件對應的配置也需要有比較完善的管理方式。

優勢

正是基於配置的特殊性,所以Apollo從設計之初就立志於成為一個有治理能力的配置發布平台,目前提供了以下的特性:

  • 統一管理不同環境、不同集群的配置
    • Apollo提供了一個統一界面集中式管理不同環境(environment)、不同集群(cluster)、不同命名空間(namespace)的配置。
    • 同一份代碼部署在不同的集群,可以有不同的配置,比如zookeeper的地址等
    • 通過命名空間(namespace)可以很方便地支持多個不同應用共享同一份配置,同時還允許應用對共享的配置進行覆蓋
  • 配置修改實時生效(熱發布)
    • 用戶在Apollo修改完配置並發布后,客戶端能實時(1秒)接收到最新的配置,並通知到應用程序
  • 版本發布管理
    • 所有的配置發布都有版本概念,從而可以方便地支持配置的回滾
  • 灰度發布
    • 支持配置的灰度發布,比如點了發布后,只對部分應用實例生效,等觀察一段時間沒問題后再推給所有應用實例
  • 權限管理、發布審核、操作審計
    • 應用和配置的管理都有完善的權限管理機制,對配置的管理還分為了編輯和發布兩個環節,從而減少人為的錯誤。
    • 所有的操作都有審計日志,可以方便地追蹤問題
  • 客戶端配置信息監控
    • 可以在界面上方便地看到配置在被哪些實例使用
  • 提供Java和.Net原生客戶端
    • 提供了Java和.Net的原生客戶端,方便應用集成
    • 支持Spring Placeholder, Annotation和Spring Boot的ConfigurationProperties,方便應用使用(需要Spring 3.1.1+)
    • 同時提供了Http接口,非Java和.Net應用也可以方便地使用
  • 提供開放平台API
    • Apollo自身提供了比較完善的統一配置管理界面,支持多環境、多數據中心配置管理、權限、流程治理等特性。不過Apollo出於通用性考慮,不會對配置的修改做過多限制,只要符合基本的格式就能保存,不會針對不同的配置值進行針對性的校驗,如數據庫用戶名、密碼,Redis服務地址等
    • 對於這類應用配置,Apollo支持應用方通過開放平台API在Apollo進行配置的修改和發布,並且具備完善的授權和權限控制
  • 部署簡單
    • 配置中心作為基礎服務,可用性要求非常高,這就要求Apollo對外部依賴盡可能地少
    • 目前唯一的外部依賴是MySQL,所以部署非常簡單,只要安裝好Java和MySQL就可以讓Apollo跑起來
    • Apollo還提供了打包腳本,一鍵就可以生成所有需要的安裝包,並且支持自定義運行時參數

核心概念

  1. application (應用)
    1. 這個很好理解,就是實際使用配置的應用,Apollo客戶端在運行時需要知道當前應用是誰,從而可以去獲取對應的配置
    2. 每個應用都需要有唯一的身份標識 -- appId,我們認為應用身份是跟着代碼走的,所以需要在代碼中配置,具體信息請參見Java客戶端使用指南
  2. environment (環境)
    1. 配置對應的環境,Apollo客戶端在運行時需要知道當前應用處於哪個環境,從而可以去獲取應用的配置
    2. 我們認為環境和代碼無關,同一份代碼部署在不同的環境就應該能夠獲取到不同環境的配置
    3. 所以環境默認是通過讀取機器上的配置(server.properties中的env屬性)指定的,不過為了開發方便,我們也支持運行時通過System Property等指定,具體信息請參見Java客戶端使用指南
  3. cluster (集群)
    1. 一個應用下不同實例的分組,比如典型的可以按照數據中心分,把上海機房的應用實例分為一個集群,把北京機房的應用實例分為另一個集群。
    2. 對不同的cluster,同一個配置可以有不一樣的值,如zookeeper地址。
    3. 集群默認是通過讀取機器上的配置(server.properties中的idc屬性)指定的,不過也支持運行時通過System Property指定,具體信息請參見Java客戶端使用指南
  4. namespace (命名空間)
    1. 一個應用下不同配置的分組,可以簡單地把namespace類比為文件,不同類型的配置存放在不同的文件中,如數據庫配置文件,RPC配置文件,應用自身的配置文件等
    2. 應用可以直接讀取到公共組件的配置namespace,如DAL,RPC等
    3. 應用也可以通過繼承公共組件的配置namespace來對公共組件的配置做調整,如DAL的初始數據庫連接數

設計

圖片

總體設計

  • 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. 客戶端和服務端保持了一個長連接,從而能第一時間獲得配置更新的推送。
  2. 客戶端還會定時從Apollo配置中心服務端拉取應用的最新配置。
    1. 這是一個fallback機制,為了防止推送機制失效導致配置不更新
    2. 客戶端定時拉取會上報本地版本,所以一般情況下,對於定時拉取的操作,服務端都會返回304 - Not Modified
    3. 定時頻率默認為每5分鍾拉取一次,客戶端也可以通過在運行時指定System Property:apollo.refreshInterval來覆蓋,單位為分鍾。
  3. 客戶端從Apollo配置中心服務端獲取到應用的最新配置后,會保存在內存中
  4. 客戶端會把從服務端獲取到的配置在本地文件系統緩存一份
    1. 在遇到服務不可用,或網絡不通的時候,依然能從本地恢復配置
  5. 應用程序從Apollo客戶端獲取最新的配置、訂閱配置更新通知

配置更新推送實現

前面提到了Apollo客戶端和服務端保持了一個長連接,從而能第一時間獲得配置更新的推送。

長連接實際上我們是通過Http Long Polling實現的,具體而言:

  • 客戶端發起一個Http請求到服務端
  • 服務端會保持住這個連接60秒
    • 如果在60秒內有客戶端關心的配置變化,被保持住的客戶端請求會立即返回,並告知客戶端有配置變化的namespace信息,客戶端會據此拉取對應namespace的最新配置
    • 如果在60秒內沒有客戶端關心的配置變化,那么會返回Http狀態碼304給客戶端
  • 客戶端在收到服務端請求后會立即重新發起連接,回到第一步

考慮到會有數萬客戶端向服務端發起長連,在服務端我們使用了async servlet(Spring DeferredResult)來服務Http Long Polling請求。

客戶端使用

https://github.com/ctripcorp/apollo/wiki/Java%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97#31-api%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F

服務端設計

配置發布后的實時推送設計

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

圖片

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

  1. 用戶在Portal操作配置發布
  2. Portal調用Admin Service的接口操作發布
  3. Admin Service發布配置后,發送ReleaseMessage給各個Config Service
  4. Config Service收到ReleaseMessage后,通知對應的客戶端

2.1.1 發送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,參見DatabaseMessageSender
  2. Config Service有一個線程會每秒掃描一次ReleaseMessage表,看看是否有新的消息記錄,參見ReleaseMessageScanner
  3. Config Service如果發現有新的消息記錄,那么就會通知到所有的消息監聽器(ReleaseMessageListener),如NotificationControllerV2,消息監聽器的注冊過程參見ConfigServiceAutoConfiguration
  4. NotificationControllerV2得到配置發布的AppId+Cluster+Namespace后,會通知對應的客戶端

示意圖如下:

圖片

2.1.2 Config Service通知客戶端的實現方式

上一節中簡要描述了NotificationControllerV2是如何得知有配置發布的,那NotificationControllerV2在得知有配置發布后是如何通知到客戶端的呢?

實現方式如下:

  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的最新配置。

本地開發模式

Apollo客戶端還支持本地開發模式,這個主要用於當開發環境無法連接Apollo服務器的時候,比如在郵輪、飛機上做相關功能開發。

在本地開發模式下,Apollo只會從本地文件讀取配置信息,不會從Apollo服務器讀取配置。

可以通過下面的步驟開啟Apollo本地開發模式。

修改環境

修改/opt/settings/server.properties(Mac/Linux)或

C:\opt\settings\server.properties(Windows)文件,設置env為Local:

env=Local

更多配置環境的方式請參考1.2.2 Environment

准備本地配置文件

在本地開發模式下,Apollo客戶端會從本地讀取文件,所以我們需要事先准備好配置文件。

本地配置目錄

本地配置目錄位於:

  • Mac/Linux: /opt/data/{appId}/config-cache
  • Windows: C:\opt\data{appId}\config-cache

appId就是應用的appId,如100004458。

請確保該目錄存在,且應用程序對該目錄有讀權限。

【小技巧】推薦的方式是先在普通模式下使用Apollo,這樣Apollo會自動創建該目錄並在目錄下生成配置文件。

本地配置文件

本地配置文件需要按照一定的文件名格式放置於本地配置目錄下,文件名格式如下:

{appId}+{cluster}+{namespace}.properties

  • appId就是應用自己的appId,如100004458
  • cluster就是應用使用的集群,一般在本地模式下沒有做過配置的話,就是default
  • namespace就是應用使用的配置namespace,一般是application

文件內容以properties格式存儲,比如如果有兩個key,一個是request.timeout,另一個是batch,那么文件內容就是如下格式:

request.timeout=2000
batch=2000

修改配置

在本地開發模式下,Apollo不會實時監測文件內容是否有變化,所以如果修改了配置,需要重啟應用生效。

樣例代碼:

用戶管理:使用Apollo提供的Spring Security簡單認證

Apollo是配置管理系統,會提供權限管理(Authorization),理論上是不負責用戶登錄認證功能的實現(Authentication)。

所以Apollo定義了一些SPI用來解耦,Apollo接入登錄的關鍵就是實現這些SPI。

可能很多公司並沒有統一的登錄認證系統,如果自己實現一套會比較麻煩。Apollo針對這種情況,從0.9.0開始提供了利用Spring Security實現的Http Basic簡單認證版本。

使用步驟如下:

1. 安裝0.9.0以上版本

如果之前是0.8.0版本,需要導入apolloportaldb-v080-v090.sql

查看ApolloPortalDB,應該已經存在Users表,並有一條初始記錄。初始用戶名是apollo,密碼是admin。

Id Username Password Email Enabled
1 apollo $2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS apollo@acme.com 1

2. 重啟Portal

如果是IDE啟動的話,確保-Dapollo_profile=github,auth

3. 添加用戶

登錄系統后在 http://{portal地址}/user-manage.html 頁面添加用戶,只有超級管理員才能添加用戶, 否則會報403錯誤。

建議同時修改apollo的密碼。修改方式是在user-manage.html頁面對apollo用戶密碼進行覆蓋。user-manager.html創建用戶的邏輯是create or update,所以直接覆蓋即可。

監控相關

Apollo客戶端和服務端目前支持CAT自動打點,所以如果自己公司內部部署了CAT的話,只要引入cat-client后Apollo就會自動啟用CAT打點。

如果不使用CAT的話,也不用擔心,只要不引入cat-client,Apollo是不會啟用CAT打點的。

更多信息,可以參考v0.4.0 Release Note

官網傳送門

https://github.com/ctripcorp/apollo


免責聲明!

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



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