微服务中集成分布式配置中心 Apollo
背景
隨著業務的發展、微服務架構的升級,服務的數量、程序的配置日益增多(各種微服務、各種服務器地址、各種參數),傳統的配置文件方式和數據庫的方式已無法滿足開發人員對配置管理的要求:配置修改后實時生效,灰度發布,分環境、分集群管理配置,完善的權限、審核機制。分布式環境下,這些配置更加復雜。
因此,我們需要配置中心來統一管理配置!把業務開發者從復雜以及繁瑣的配置中解脫出來,只需專注于業務代碼本身,從而能夠顯著提升開發以及運維效率。同時將配置和發布包解藕也進一步提升發布的成功率,并為運維的細力度管控、應急處理等提供強有力的支持。
在之前的文章中,我們介紹過 Spring Cloud 中的分布式配置中心組件:Spring Cloud Config。本文將會介紹功能更為強大的 Apollo。
分布式配置中心
在一個分布式環境中,同類型的服務往往會部署很多實例。這些實例使用了一些配置,為了更好地維護這些配置就產生了配置管理服務。通過這個服務可以輕松地管理成千上百個服務實例的配置問題。配置中心的特點:
- 配置的增刪改查;
- 不同環境配置隔離(開發、測試、預發布、灰度/線上);
- 高性能、高可用性;
- 請求量多、高并發;
- 讀多寫少;
現有的配置中心組件有:Spring Cloud Config、Apollo、Disconf、Diamond 等等,這些組件在功能上有或多或少的差異,但是都具有基本的配置中心的功能。
Apollo 簡介
Apollo(阿波羅)是攜程框架部門研發的開源配置管理中心,能夠集中化管理應用不同環境、不同集群的配置,配置修改后能夠實時推送到應用端,并且具備規范的權限、流程治理等特性。目前的有超過 14k 的 star,使用廣泛。Apollo基于開源模式開發,開源地址:github.com/ctripcorp/a…
圖片來源 Apollo首先用戶在配置中心對配置進行修改并發布;配置中心通知Apollo客戶端有配置更新;Apollo客戶端從配置中心拉取最新的配置、更新本地配置并通知到應用。
Apollo 支持4個維度管理 Key-Value 格式的配置:
- application (應用):實際使用配置的應用,Apollo客戶端在運行時需要知道當前應用是誰,從而可以去獲取對應的配置;每個應用都需要有唯一的身份標識 -- appId,應用身份是跟著代碼走的,所以需要在代碼中配置。
- environment (環境):配置對應的環境,Apollo客戶端在運行時需要知道當前應用處于哪個環境,從而可以去獲取應用的配置。
- cluster (集群):一個應用下不同實例的分組,比如典型的可以按照數據中心分,把上海機房的應用實例分為一個集群,把北京機房的應用實例分為另一個集群。對不同的cluster,同一個配置可以有不一樣的值,如zookeeper地址。
- namespace (命名空間):一個應用下不同配置的分組,可以簡單地把namespace類比為文件,不同類型的配置存放在不同的文件中,如數據庫配置文件,RPC配置文件,應用自身的配置文件等;應用可以直接讀取到公共組件的配置namespace,如DAL,RPC等;應用也可以通過繼承公共組件的配置namespace來對公共組件的配置做調整,如DAL的初始數據庫連接數。
我們在集成 Apollo 時,可以根據需要創建相應的維度。
快速入門
下面我們搭建一個基于 Spring Boot 的微服務,集成 Apollo。
啟動服務端
Apollo配置中心包括:Config Service、Admin Service 和 Portal。
- Config Service:提供配置獲取接口、配置推送接口,服務于Apollo客戶端;
- Admin Service:提供配置管理接口、配置修改發布接口,服務于管理界面Portal;
- Portal:配置管理界面,通過MetaServer獲取AdminService的服務列表,并使用客戶端軟負載SLB方式調用AdminService。
官網準備好了一個Quick Start安裝包,大家只需要下載到本地,就可以直接使用,免去了編譯、打包過程。也可以自行編譯,較為繁瑣。
Apollo服務端共需要兩個數據庫:ApolloPortalDB和ApolloConfigDB。創建的語句見安裝包,創建好之后需要配置啟動的腳本,即 demo.sh 腳本:
#apollo config db info apollo_config_db_url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8 apollo_config_db_username=用戶名 apollo_config_db_password=密碼(如果沒有密碼,留空即可)# apollo portal db info apollo_portal_db_url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8 apollo_portal_db_username=用戶名 apollo_portal_db_password=密碼(如果沒有密碼,留空即可) 復制代碼腳本會在本地啟動3個服務,分別使用8070, 8080, 8090端口,請確保這3個端口當前沒有被使用。執行
./demo.sh start 復制代碼看到輸出如下的日志信息:
==== starting service ==== Service logging file is ./service/apollo-service.log Started [10768] Waiting for config service startup....... Config service started. You may visit http://localhost:8080 for service status now! Waiting for admin service startup.... Admin service started ==== starting portal ==== Portal logging file is ./portal/apollo-portal.log Started [10846] Waiting for portal startup...... Portal started. You can visit http://localhost:8070 now! 復制代碼Apollo 服務端啟動成功。
客戶端應用
搭建好 Apollo 服務器之后,接下來將我們的應用接入 Apollo。
引入依賴
<dependency><groupId>com.ctrip.framework.apollo</groupId><artifactId>apollo-client</artifactId><version>1.1.0</version></dependency> 復制代碼在依賴中只需要增加 apollo-client 的引用。
入口程序
("TEST1.product") public class ApolloApplication {public static void main(String[] args) {SpringApplication.run(ApolloApplication.class, args);} } 復制代碼我們通過 @EnableApolloConfig("TEST1.product") 注解開啟注冊到 Apollo 服務端,并指定了 namespace 為 TEST1.product。
配置文件
app.id: spring-boot-logger# set apollo meta server address, adjust to actual address if necessary apollo.meta: http://localhost:8080 server: port: 0 復制代碼配置文件中指定了appid 和 Apollo 服務器的地址。
測試應用
我們通過動態設置輸出的日志等級來測試接入的配置中心。
public class LoggerConfiguration {private static final Logger logger = LoggerFactory.getLogger(LoggerConfiguration.class);private static final String LOGGER_TAG = "logging.level.";private LoggingSystem loggingSystem;private Config config;// 監聽 Apollo 配置中心的刷新事件private void onChange(ConfigChangeEvent changeEvent) {refreshLoggingLevels();}// 設置刷新之后的日志級別private void refreshLoggingLevels() {Set<String> keyNames = config.getPropertyNames();for (String key : keyNames) {if (containsIgnoreCase(key, LOGGER_TAG)) {String strLevel = config.getProperty(key, "info");LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());loggingSystem.setLogLevel(key.replace(LOGGER_TAG, ""), level);logger.info("{}:{}", key, strLevel);}}}private static boolean containsIgnoreCase(String str, String searchStr) {if (str == null || searchStr == null) {return false;}int len = searchStr.length();int max = str.length() - len;for (int i = 0; i <= max; i++) {if (str.regionMatches(true, i, searchStr, 0, len)) {return true;}}return false;} } 復制代碼如上的配置類用于根據 Apollo 配置中心的日志等級配置,設置本地服務的日志等級,并監聽刷新事件,將刷新后的配置及時應用到本地服務,其中 @PostConstruct 注解用于在完成依賴項注入以執行任何初始化之后需要執行的方法。
public class PrintLogger {private static Logger logger = LoggerFactory.getLogger(PrintLogger.class);("${kk.v}")private String v;public void printLogger() throws Exception {Executors.newSingleThreadExecutor().submit(() -> {while (true) {logger.error("=========" + v);logger.info("我是info級別日志");logger.error("我是error級別日志");logger.warn("我是warn級別日志");logger.debug("我是debug級別日志");TimeUnit.SECONDS.sleep(1);}});} } 復制代碼起一個線程,輸出不同級別的日志。根據配置的日志等級,過濾后再打印。我們在如上的程序中,還自定義了一個字段,同樣用以測試隨機打印最新的值。
測試
我們在 Apollo 的配置界面中,增加如下的配置:
并將配置發布,啟動我們本地的 SpringBoot 服務:
2019-05-28 20:31:36.688 ERROR 44132 --- [pool-1-thread-1] com.blueskykong.apollo.PrintLogger : =========log-is-error-level. 2019-05-28 20:31:36.688 ERROR 44132 --- [pool-1-thread-1] com.blueskykong.apollo.PrintLogger : 我是error級別日志 復制代碼我們將調整日志的級別為warn,只需要在界面上編輯。
將編輯好的配置發布,應用服務將會收到刷新事件。
可以看到,服務刷新了日志的級別,打印 warn 的日志信息。 2019-05-28 20:35:56.819 WARN 44132 --- [pool-1-thread-1] com.blueskykong.apollo.PrintLogger : 我是warn級別日志 2019-05-28 20:36:06.823 ERROR 44132 --- [pool-1-thread-1] com.blueskykong.apollo.PrintLogger : =========log-is-warn-level. 復制代碼原理細究
在體驗了 Apollo 作為配置中心之后,我們將了解下 Apollo 的總體設計和實現的原理。
Apollo 整體架構
圖片來源 Apollo上圖簡要描述了 Apollo 的總體設計,從下往上看:
- 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進程中
ConfigService、AdminService、Portal 屬于 Apollo 服務端的模塊,其中提到的 Eureka 是為了保證高可用,Config 和 Admin 都是無狀態以集群方式部署的,Client 怎么找到 Config?Portal 怎么找到 Admin?為了解決這個問題,Apollo在其架構中引入了Eureka服務注冊中心組件,實現微服務間的服務注冊和發現用于服務發現和注冊,Config 和 Admin Service注冊實例并定期報心跳, Eureka 與 ConfigService 一起部署。
MetaServer 其實是一個Eureka的Proxy,將Eureka的服務發現接口以更簡單明確的HTTP接口的形式暴露出來,方便 Client/Protal 通過簡單的 HTTPClient 就可以查詢到 Config/Admin 的地址列表。獲取到服務實例地址列表之后,再以簡單的客戶端軟負載(Client SLB)策略路由定位到目標實例,并發起調用。
客戶端實現
在配置中心中,一個重要的功能就是配置發布后實時推送到客戶端。下面我們簡要看一下這塊是怎么設計實現的。
上圖簡要描述了配置發布的大致過程:用戶在Portal操作配置發布;Portal調用Admin Service的接口操作發布;Admin Service發布配置后,發送ReleaseMessage給各個Config Service;Config Service收到ReleaseMessage后,通知對應的客戶端。
如何通知客戶端呢?我們看到 Apollo 的實現步驟如下:
- 這是一個fallback機制,為了防止推送機制失效導致配置不更新
- 客戶端定時拉取會上報本地版本,所以一般情況下,對于定時拉取的操作,服務端都會返回304 - Not Modified
- 定時頻率默認為每5分鐘拉取一次,客戶端也可以通過在運行時指定System Property: apollo.refreshInterval來覆蓋,單位為分鐘。
小結
本文首先介紹分布式配置中心的概念和 Apollo 接入的實踐,然后深入介紹了 Apollo 的總體架構和實現的一些細節??偟脕碚f, Apollo 是現有配置中心組件中,功能最全的一個。能夠集中化管理應用不同環境、不同集群的配置,配置修改后能夠實時推送到應用端,并且具備規范的權限、流程治理等特性,適用于微服務配置管理場景。
本文對應的代碼地址: github.com/keets2012/S…
訂閱最新文章,歡迎關注我的公眾號
參考
轉載于:https://juejin.im/post/5ced367d518825333359d0fb
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的微服务中集成分布式配置中心 Apollo的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 面试高频问题:HashMap实现原理
- 下一篇: psycopg2模块安装问题