隨着業務的發展、微服務架構的升級,服務的數量、程序的配置日益增多(各類微服務、各類服務器地址、各類參數),傳統的配置文件方式和數據庫的方式已沒法知足開發人員對配置管理的要求:配置修改後實時生效,灰度發佈,分環境、分集羣管理配置,完善的權限、審覈機制。分佈式環境下,這些配置更加複雜。java
所以,咱們須要配置中心來統一管理配置!把業務開發者從複雜以及繁瑣的配置中解脫出來,只需專一於業務代碼自己,從而可以顯著提高開發以及運維效率。同時將配置和發佈包解藕也進一步提高發布的成功率,併爲運維的細力度管控、應急處理等提供強有力的支持。mysql
在以前的文章中,咱們介紹過 Spring Cloud 中的分佈式配置中心組件:Spring Cloud Config。本文將會介紹功能更爲強大的 Apollo。git
在一個分佈式環境中,同類型的服務每每會部署不少實例。這些實例使用了一些配置,爲了更好地維護這些配置就產生了配置管理服務。經過這個服務能夠輕鬆地管理成千上百個服務實例的配置問題。配置中心的特色:github
現有的配置中心組件有:Spring Cloud Config、Apollo、Disconf、Diamond 等等,這些組件在功能上有或多或少的差別,可是都具備基本的配置中心的功能。spring
Apollo(阿波羅)是攜程框架部門研發的開源配置管理中心,可以集中化管理應用不一樣環境、不一樣集羣的配置,配置修改後可以實時推送到應用端,而且具有規範的權限、流程治理等特性。目前的有超過 14k 的 star,使用普遍。Apollo基於開源模式開發,開源地址:github.com/ctripcorp/a…sql
首先用戶在配置中心對配置進行修改併發布;配置中心通知Apollo客戶端有配置更新;Apollo客戶端從配置中心拉取最新的配置、更新本地配置並通知到應用。數據庫
Apollo 支持4個維度管理 Key-Value 格式的配置:緩存
咱們在集成 Apollo 時,能夠根據須要建立相應的維度。bash
下面咱們搭建一個基於 Spring Boot 的微服務,集成 Apollo。服務器
Apollo配置中心包括:Config Service、Admin Service 和 Portal。
官網準備好了一個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
的引用。
@SpringBootApplication
@EnableApolloConfig("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 服務器的地址。
咱們經過動態設置輸出的日誌等級來測試接入的配置中心。
@Service
public class LoggerConfiguration {
private static final Logger logger = LoggerFactory.getLogger(LoggerConfiguration.class);
private static final String LOGGER_TAG = "logging.level.";
@Autowired
private LoggingSystem loggingSystem;
@ApolloConfig
private Config config;
@ApolloConfigChangeListener
// 監聽 Apollo 配置中心的刷新事件
private void onChange(ConfigChangeEvent changeEvent) {
refreshLoggingLevels();
}
@PostConstruct
// 設置刷新以後的日誌級別
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
註解用於在完成依賴項注入以執行任何初始化以後須要執行的方法。
@Service
public class PrintLogger {
private static Logger logger = LoggerFactory.getLogger(PrintLogger.class);
@ApolloJsonValue("${kk.v}")
private String v;
@PostConstruct
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,只須要在界面上編輯。
將編輯好的配置發佈,應用服務將會收到刷新事件。
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 的整體設計,從下往上看:
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 的實現步驟以下:
本文首先介紹分佈式配置中心的概念和 Apollo 接入的實踐,而後深刻介紹了 Apollo 的整體架構和實現的一些細節。總得來講, Apollo 是現有配置中心組件中,功能最全的一個。可以集中化管理應用不一樣環境、不一樣集羣的配置,配置修改後可以實時推送到應用端,而且具有規範的權限、流程治理等特性,適用於微服務配置管理場景。
本文對應的代碼地址: github.com/keets2012/S…