攜程Apollo配置中心介紹以及實現細節
在瞭解攜程Applo配置中心之前,我們需要在心裡思考以下這些問題:
- 什麼是配置中心,如果我們自己去實現一個配置中心,它應該具備哪些功能;
- 我們用過哪些配置中心,它們有什麼區別;
- 如何實現一個配置中心;
一、配置中心概述
1.1 配置中心的由來
由於微服務框架的興起,以前一個大型專案,有許多個模組,比如使用者模組、訂單模組、商品模組、庫存模組等,整個專案可能單單java檔案就能有幾百上千個。這種大專案打一次包可能就要幾十分鐘甚至幾個小時,而且這種大專案在一個伺服器上跑,浪費的資源是十分大的,而且訪問量也低,這就是早期的單體專案。後來人們將這個大專案按模組拆開,各個模組自己作為一個web專案,彼此之間通過網路進行伺服器間的通訊(RPC、HTTP等),各個模組自己隨意部署,就漸漸演變成微服務專案。
以Spring Cloud微服務專案為例,我們將大專案拆成小專案後,我們的application.yml(或者applicaion.proprties)配置檔案也同樣變成了好幾份,即便你兩個小專案用的是同樣的配置,你也要複製兩次。一旦我們的小專案數量多了起來,那麼管理這些配置將會變得困難。
而且一旦某個專案部署了十幾二十個機器上後,你開發修改了某一個配置項,那就意味這必須重啟這十幾二十個機器上的專案。為了避免這種事情的發生,衍生出了一個專案專門管理配置項,也就是配置中心。
1.2 配置中心功能
配置中心的核心功能主要包含:
- 抽象標準化:配置中心應該遮蔽掉其實現的細節,方便使用者進行自主式配置管理,一般使用者只需要關注兩個抽象和標準化的介面即可:
- 配置管理介面UI,方便應用開發人員管理和釋出配置;
-
封裝好的客戶端API,方便應用整合和獲取配置;
- 高可用:如果大量服務的執行依賴於配置中心,當配置中心宕機時,會影響大面積服務的執行。
- 多環境多叢集:微服務應用大多采用多環境部署,一般標準的環境有開發/測試/生產等,有的應用還需要多叢集部署,如支援跨機房或多版本部署。配置中心需要支援對多環境和多叢集應用配置的集中式管理;
- 實時性:配置更新需要儘快通知到客戶端,有些配置的實時性要求很高,像是主備切換配置,這些需要秒級切換配置的能力。
- 治理:配置需要治理,具體包括:
- 配置格式校驗:應用的配置資料儲存在配置中心一般都會以一種配置格式儲存,比如properties、json、yml等,如果配置格式錯誤,會導致客戶端解析配置失敗引起生產故障,配置中心對配置的格式校驗能夠有效防止人為錯誤操作的發生,是配置中心核心功能中的剛需;
-
配置審計:需要記錄修改人,修改內容和修改事件,方便出現問題時能後追溯;
-
配置版本控制:每次變更需要版本化,出現問題可以及時回滾到上一版本的配置;
-
配置許可權控制:配置變更釋出需要認證授權;
-
灰度釋出:配置釋出時可以先讓少數例項生效,確定沒有問題就可以擴大應用範圍。
- 配置格式校驗:應用的配置資料儲存在配置中心一般都會以一種配置格式儲存,比如properties、json、yml等,如果配置格式錯誤,會導致客戶端解析配置失敗引起生產故障,配置中心對配置的格式校驗能夠有效防止人為錯誤操作的發生,是配置中心核心功能中的剛需;
- 監聽查詢:當排查問題或者進行統計的時候,需要知道一個配置被哪些應用例項使用到,以及一個例項使用到了哪些配置。
1.3 常用的配置中心
目前市面上流行的配置中心種類繁多,常用的主要有以下幾種(下表來源自其它部落格):
功能點 | Spring Cloud Config | Apollo | Nacos |
開源時間 | 2014.9 | 2016.5 | 2018.6 |
配置實時推送 | 支援(Spring Cloud Bus) | 支援(HTTP長輪詢1S內) | 支援(HTTP長輪詢1S內) |
版本管理 | 支援(Git) | 支援 | 支援 |
配置回滾 | 支援(Git) | 支援 | 支援 |
灰度釋出 | 支援 | 支援 | 不支援 |
許可權管理 | 支援 | 支援 | 不支援 |
多叢集 | 支援 | 支援 | 支援 |
多環境 | 支援 | 支援 | 支援 |
監聽查詢 | 支援 | 支援 | 支援 |
多語言 | 支支援java | go、c++、python、php、.net等 | node.js、c++、python、java等 |
單機部署 | Config-Server + Git + Spring Cloud Bus | Apollo-quickstart + mysql | nacos單節點 |
分散式部署 | Config-Server + Git + MQ | Config + Admin + Portal + mysql | nacos + mysql |
配置格式支援 | 不支援 | 支援 | 支援 |
通訊協議 | HTTP和AMQP | HTTP | HTTP |
資料一致性 | Git保證資料一致性、Config-server從Git讀資料 | 資料庫模擬訊息佇列,Apollo定時讀訊息 | HTTP非同步通知 |
二、Apollo配置中心設計
Apollo(阿波羅)是攜程框架部門研發的分散式配置中心,能夠集中化管理應用不同環境、不同叢集的配置,配置修改後能夠實時推送到應用端,並且具備規範的許可權、流程治理等特性,適用於微服務配置管理場景。
2.1 Apollo基本模型
2.2 架構模組
2.3 服務端設計
2.4 客戶端設計
以上內容摘自攜程Apollo配置中心官網,關於Apollo配置中心的設計官網已經寫的很詳細了,這裡不過多設計。官網介紹:https://www.apolloconfig.com/#/zh/design/apollo-design。
三、原始碼分析
最近看了一部分Apollo的原始碼,由於程式碼寫的比較早,資料庫當時使用的還是hibernate,服務間呼叫採用的RetryableRestTemplate(作者自己對RestTemplate進行了封裝,加了重試機制),並且程式碼中使用到了大量事件監聽者模式,理清了業務邏輯後,程式碼還是很容易讀懂的,這裡就不做具體介紹了。原始碼分析請移步芋道原始碼。
四、多環境、多叢集的實現
4.1、問題思考
Q1、先來說一下多環境的實現,我們的環境一般分為開發環境、測試環境、生產環境,那如何保證指定環境啟動時服務能正確讀取到配置中心上相應環境的配置檔案呢?
Q2、以開發環境為例,一個大型分散式微服務系統會有很多微服務子專案,那麼在開發環境怎麼對這些微服務配置進行管理呢?
Q3、再來說一下多叢集的實現,配置中心如何支援同一個服務跨機房和多版本部署時使用不同的配置?
4.2 Apollo實現思路
我們先來介紹一下Apollo的實現,Apollo採用的是在每個環境下部署一個Config Service、Admin Service、以及我們的應用,服務發現是通過Eureka實現的,這樣各個環境的程式註冊到對應環境的Eureka上。這樣就可以解決我們第一個問題。
Apollo會維護一個應用列表,應用列表中的每個應用由我們建立,主要包含應用名稱、應用id等欄位,我們通過該應用id來標識每個應用,就可以解決第二個問題。
針對第三個問題,Apollo加入了叢集管理,通過為應用新增叢集,將一個應用劃分為若干個叢集,可以使同一服務在不同的叢集(如不同的資料中心,針對多版本情況,我們可以把相同版本的同一個服務劃分為一個叢集)使用不同的配置。
Apollo配置中心也有名稱空間的概念,一個叢集由若干個名稱空間,每個名稱空間中就包含我們的配置資訊。以Spring Boot應用的application.properties檔案為例,我們可以將application.properties拆分為若干份,比如資料庫配置的、日誌配置的、許可權配置的,每一份對應一個名稱空間。
Apollo中心的本質就是維護一個應用列表。下圖是Apollo配置中心其中一個應用的全部資訊,一個應用主要由專案資訊、環境列表、叢集、名稱空間組成。
從上圖中,我們發現Apollo配置資訊是以key-value方式存放的。
其實體ER圖大致如下(下圖只繪製了部分欄位,你去看官方給的指令碼,你會發現表中存在大量欄位冗餘以及同一欄位資料長度在不同表設定還不一樣,MMP...):
Apollo中Portal服務是多個環境公用,通過在Portal服務中配置每個環境下Admin Service的地址,實現直接管理各個環境的配置。
4.3 自己實現
由於Apollo在不同的環境下都要部署一套Config Service、Admin Service,部署起來麻煩。
我們可以參考nacos的實現,只部署一套nacos服務,實現多環境下得配置管理。
我們採用名稱空間的設計思想, 由Namespace + Group + Data ID 共同構建(類似Java裡的包名 類名 泛型):
- namespace是可以用於區分部署環境的;
- Group是用來對服務分組的;
- Data ID是用來區分具體的服務;
預設情況: Namespace=public,Group=DEFAULT_GROUP。
Namespace預設是public,Namespace主要用來實現環境隔離。 比方說我們現在有三個環境:開發、測試、生產,我們就可以建立三個Namespace,不同的Namespace之間是隔離的。
Group預設是DEFAULT_GROUP,Group主要用來對服務分組。一個服務可以包含多個Cluster(叢集),比如我們常說的zookeeper、kafka、hadoop等叢集,我們在杭州機房的入若干臺機器上分別部署zookeeper服務,組成Zookeeper叢集A;然後我們又在廣州機房的入若干臺機器上分別部署zookeeper服務,組成Zookeeper叢集B,叢集A和叢集B分別使用不同的配置。這裡的叢集A的配置就對應我們的組A和zookeeper服務的Data ID確定的配置,叢集B的配置就對應我們的組B和zookeeper服務的Data ID確定的配置。
綜上所述,我們需要建立一張資料表config_namespace:
id | namespace_id | namesapce_name | remark | create_time | create_by | update_time | update_by |
主鍵 | 名稱空間唯一識別符號 | 名稱空間名稱 | 描述資訊 | 建立時間/釋出時間 | 建立者 | 更新時間 | 更新者 |
這裡我們並沒有去建立group、應用表。這些資訊在為一個應用建立配置資訊時指定即可。
五、服務治理的實現
5.1 配置格式校驗
配置檔案的格式一般有properties、yml、json、xml、text這幾種。配置資訊我們以字串的資訊存放,如圖:
這裡儲存(為了簡單,我們儲存就是釋出)時其實就是呼叫相應的介面,在各個介面中對各種格式的資料進行校驗,校驗通過時儲存到資料庫。
以properties檔案為例,為了簡單,我們可以建立一個數據表config_content:
id | data_id | group_name | content | format | remark | create_time | create_by | update_time | update_by |
主鍵 | 應用id | 組id | 配置內容 | 檔案格式 | 描述資訊 | 建立時間/釋出時間 | 建立者 | 更新時間 | 更新者 |
將上圖配置內容儲存在content欄位中即可,而Apollo是將properties檔案中每一個鍵值對儲存成一條記錄,存放在Item表中的。而其它格式檔案Apollo儲存為一條記錄。
這裡我們為了統一,所以就將配置資訊都放在content中,使用一條記錄儲存。
5.2配置版本控制
我們需要在每次對配置進行新增、刪除、修改的時候,記錄下配置的變更資訊。以Apollo Commit為例:
我們可以發現Commit表的ChangeSet儲存了我們每次對配置的操作記錄。ChangeSet是一個json字串,主要包含三塊內容createItems、updateItems、deleteItems,比如我們將某個namespace的key為key4的配置項的值從value4123修改為value41234,將會產生一條這樣的紀錄:
{ "createItems": [ ], "updateItems": [ { "oldItem": { "namespaceId": 32, "key": "key4", "value": "value4123", "comment": "123", "lineNum": 4, "id": 15, "isDeleted": false, "dataChangeCreatedBy": "apollo", "dataChangeCreatedTime": "2018-04-27 16:49:59", "dataChangeLastModifiedBy": "apollo", "dataChangeLastModifiedTime": "2018-04-27 22:37:52" }, "newItem": { "namespaceId": 32, "key": "key4", "value": "value41234", "comment": "123", "lineNum": 4, "id": 15, "isDeleted": false, "dataChangeCreatedBy": "apollo", "dataChangeCreatedTime": "2018-04-27 16:49:59", "dataChangeLastModifiedBy": "apollo", "dataChangeLastModifiedTime": "2018-04-27 22:38:58" } } ], "deleteItems": [ ] }
同理,我們也可以採用類似的方式,建立一張config_commit表,用來記錄每次對配置的增加、刪除、修改資訊。
id | content_id | insert | update_before | update_after | delete | create_time | create_by |
主鍵 | 應用id | 插入內容 | 更新前內容 | 更新後內容 | 刪除內容 | 建立時間 | 建立者 |
5.3配置審計
配置審計其實就可以看作日誌記錄,只不過這個日誌記錄只是記錄使用者對配置資訊的操作, 主要記錄某人在某時做了什麼配置操作。
參考文章:
[3] Apollo官網指導手冊