配置是程序運行時,動態調整行爲的能力。html
配置有如下屬性:git
配置是獨立於程序的只讀變量github
數據庫
配置伴隨應用的整個生命週期緩存
應用在啓動的時候經過讀取配置來初始化,在運行時根據配置改變本身的行爲。安全
配置有多中加載方式架構
程序內部hard code,這種作法是反模式,十分不建議。負載均衡
配置文件。框架
環境變量,配置能夠預置在操做系統的環境變量裏,程序運行時讀取。運維
啓動參數,能夠在程序啓動時一次性提供參數。
基於數據庫,將易變配置放在數據庫中,這樣能夠在運行期靈活調整配置。
配置須要治理
權限控制:對配置的修改須要有比較完善的權限控制,不然不正確的配置會引發災難。
不一樣環境、集羣配置管理:同一份程序在不一樣環境(開發、測試、生產)、不一樣的集羣(如不一樣的數據中心)常常須要有不一樣的配置,因此須要完善的環境、集羣配置管理。
框架類組件配置管理:如CAT客戶端的配置。
在沒有引入配置中心以前,通常研發的時候會有如下痛點:
配置格式散亂不規範
有的用properties格式,有的用yml格式,有的用xml格式,還有存DB的,作法五花八門。
主要採用本地靜態配置,配置修改麻煩
在分佈式微服務環境下,當服務實例不少的時候,配置修改費時費力。
易引起生產事故
如將測試環境的配置帶到生產環境上。
配置缺少安全審計和版本控制功能
沒法查看到修改配置的人,修改了什麼內容,以及修改的時間,除了問題也沒法及時回滾。
交付件和配置分離
傳統作法應用在打包部署的時候,會爲不一樣環境打出不一樣配置的包,每一個包裏頭包含環境特定配置。而如今推薦採用如容器鏡像方式打包和交付微服務,應用鏡像通常只打一份,能夠部署到不一樣環境,因此這要求交付件和配置進行分離,交付件只製做一份,而且是不可變的,能夠部署到任意環境。可是全部環境的配置均可以在配置中心集中配置,運行期應用能夠根據自身環境到配置中心動態拉取相應的配置。
抽象標準化
配置中心應該封裝屏蔽配置管理的細節和配置的不一樣格式,方便用戶進行自主式配置管理,通常用戶只須要關注兩個抽象和標準化的接口便可:
配置管理界面UI,方便應用開發人員管理和發佈配置。
封裝好的客戶端API,方便應用集成和獲取配置。
多環境多集羣
微服務應用大多采用多環境部署,通常標準的環境有開發/測試/生產等,有的應用還須要多集羣部署,如支持跨機房或多版本部署。配置中心須要支持對多環境和多集羣應用配置的集中式管理。
高可用
配置中心不能掛,不然會大面積影響微服務。
實時性
配置更新須要儘快通知到客戶端,有些配置的實時性要求很高,像是主備切換配置或者藍綠部署配置,這些須要秒級切換配置的能力。
治理
配置須要治理,具體包括:
配置審計,須要記錄修改人,修改內容和修改事件,方便出現問題時能後追溯。
配置版本控制,每次變動須要版本化,出現問題能夠及時回滾到上一版本。
配置權限控制,配置變動發佈須要認證受權。
灰度發佈,配置發佈時能夠先讓少數實例生效,肯定沒有問題就能夠擴大應用範圍。
Apollo是攜程框架部門研發的開源配置管理中心,可以集中化管理應用不一樣環境、不一樣集羣的配置,配置修改後可以實時推送到應用端,而且具有規範的權限、流程治理等特性。
Apollo的特性以下:
統一管理不一樣環境、不一樣集羣的配置。
Apollo提供了一個統一界面集中式管理不一樣環境、不一樣集羣、不一樣命名(namespace)空間的配置,並且同一份代碼部署在不一樣集羣,能夠有不一樣的配置。
配置修改實時生效(熱發佈)
版本發佈管理
灰度發佈
客戶端配置信息監控
能夠在界面上方便地看到配置在被哪些實例使用。
提供Java和.Net原生客戶端
提供開放平臺API
部署簡單
能夠看到,Apollo的特性幾乎符合上文配置中心的核心需求。
用戶如今UI界面修改/發佈配置。
Apollo配置中心會將配置更新信息推送到Apollo客戶端。
客戶端再從Apollo配置中心拉取最新配置,更新本地配置以後,通知到應用。
客戶端和服務端會保持一個長鏈接,從而第一時間獲取配置更新的推送。
客戶端還會定時從Apollo配置中心服務端拉取應用的最新配置,並且客戶端定時拉取會上報給本地版本,默認每隔5分鐘拉取一次,也能夠經過運行時指定apollo.refreshInterval來覆蓋,單位爲分鐘。
客戶端從Apollo配置中心服務端獲取到應用的最新配置後,會保存在內存。
客戶端會把從服務端拉取到的配置在本地文件系統緩存一份,保證在遇到服務不可用或網路故障時,依賴能從本地恢復配置,也實現了必定的高可用性。
應用程序從客戶端獲取到罪行的配置、訂閱配置更新通知。
上文提到Apollo客戶端和服務端保持了一個長鏈接,從而能夠第一時間得到配置更新的推送,實際上長鏈接是經過Http Long Polling實現的:
客戶端發起Http請求給服務端
服務端保持60s鏈接
若是在60s中有客戶端關心的配置變化,則請求會當即返回,並告知客戶端有配置變化的namespace信息,客戶端會據此拉取該namespace的最新配置。
若是60s內沒有客戶端關心的配置變化,則返回http狀態碼304給客戶端。
在服務端,是用async servlet(Spring DeferredResult)來服務Http Long Polling請求。
在捋清思路以前,先對其中的模塊進行了解:
主要模塊:
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進行配置管理。
假設沒有分佈式微服務的服務發現,那麼:
Portal會調用AdminService進行配置管理和發佈
ConfigService和Client保持長鏈接,ConfigService服務於Client進行配置獲取,ConfigService和Client經過一種推拉結合的方式,實現配置實時更新 的同時,保證配置更新不丟失。
ConfigService和AdminService共享ConfigDB,ConfigDB中存放項目在某個環境的配置信息,並且這三者在每一個配置環境(DEV/FAT/UAT/PRO)中都要部署一份。
Protal有個獨立的PortalDB,存放用戶權限、項目和配置的元數據信息。Portal只需部署一份,它能夠管理多套環境。
由於Client要找ConfigService須要地址列表,Portal找到AdminService也須要地址列表,這時候就須要服務發現了。
Config/AdminService啓動後都會註冊到Eureka服務註冊中心,並按期發送保持心跳。
Eureka採用集羣方式部署,使用分佈式 一致性協議保證每一個勢力的狀態最終一致。
Client和Portal能夠經過Eureka發現ConfigService和AdminService的地址列表。
由於攜程須要跟自家的.Net的系統兼容,因此引入了MetaServer服務,這至關因而代理Proxy,將Eureka的服務發現接口以更簡單明確的HTTP接口的形式暴露出來。
可是MetaServer自己也是無狀態以集羣方式部署的,那麼Client/Protal該如何發現MetaServer呢?傳統的方式是藉助硬件(能夠在其中指定MetaServer的位置)或者是軟件負載均衡器。
使用NginxLB(也稱Software Load Balancer),由運維爲MetaServer集羣配置一個域名,指向NginxLB集羣,NginxLB再對MetaServer進行負載均衡和流量轉發。Client/Portal經過域名+NginxLB間接訪問MetaServer集羣。