前提介紹
-
如果要設計開發一套微服務基礎架構,引數化配置是一個非常重要的點,而Netflix也開源了一個叫變色龍Archaius的配置中心客戶端,而且Archaius可以說是比其他客戶端具備更多生產級特性,也更靈活。
-
在NetflixOSS微服務技術棧中,幾乎所有的其它元件(例如Zuul, Hystrix, Eureka, Ribbon等)都依賴於Archaius,可以說理解Archaius是理解和使用Netflix其它微服務元件的基礎。
Archaius是什麼
Netflix Archaius是一個配置管理庫,其重點是來自多個配置儲存的動態屬性。它包括一組用於Netflix的Java配置管理API。它主要實現為Apache Commons Configuration庫的擴充套件。提供的主要功能有:
注意,Netflix只是開源了其配置中心的客戶端部分(也就是Archaius),沒有開源配套的伺服器端。Archaius其實是配置源實現無關的,可以對接各種配置中心作為資料來源,本文後面會介紹Archaius如何和Apollo配置中心進行整合。
Archaius專案的由來
-
在微服務環境下,配置常常需要根據不同的上下文環境進行調整,或者說配置應該是多維度的。例如在Netflix,上下文維度包括環境(開發、測試和生產)。
-
Netflix希望能夠根據釋出的環境,甚至請求的上下文,動態地調整服務的配置,讓Netflix的整個系統的行為和邏輯變得動態可調配,以適應網際網路應用快速多變的需求。為此,Netflix平臺團隊開發了配置中心產品,團隊將這個產品形象地稱為變色龍Archaius,因為變色龍這種動物能夠根據自己所處的環境動態調整身體的顏色。
Archaius在Netflix的用例場景
- 根據請求上下文開啟或關閉某項功能。
- 某個頁面預設顯示10個商品,在某些情況下,可以通過Archaius調整配置,只顯示5個商品。
- 動態調整Hystrix熔斷器的行為。
- 調整服務呼叫客戶端的連線和請求超時引數。
- 如果某個線上服務產生出錯告警,可以動態調整日誌輸出級別(粒度可以細到包或者元件級別),這樣可以通過詳細日誌排查問題。問題定位以後,再將日誌輸出級別恢復到預設級別。
- 對於多區域或者多國家部署的應用,通過動態配置,可以根據不同區域和國家開啟不同的功能。
- 可以根據使用者的實際訪問模式動態調整一些基礎中介軟體的配置,例如快取的存活時間TTL(Time To Live)。
- 資料庫訪問客戶端的連線池配置,可以對不同服務配不同的值。例如,一個請求頻率RPS(Request Per Second)小的服務,可以配置較小的連線數,而一個請求頻率大的服務,可以配置較大的連線數。
- 執行期配置的變更可以在不同維度生效,例如叢集中的單個例項維度,多區域部署下的某個區域維度,某個服務棧維度,或者某個應用叢集維度。
- 功能開關(Feature Flag)釋出,有些功能雖然上線,但是並不馬上啟用,而是通過配置開關動態啟用,這樣可以根據情況靈活開啟或者關閉某項線上功能。
- 金絲雀釋出(Canary Release),新功能上線時,讓新老叢集同時並存一段時間,通過配置將到老叢集的流量逐步動態調整到新叢集,如果監控顯示無異常,則完成新叢集的上線,如異常,則快速切回老叢集。
Archaius的技術基礎
-
archaius是netflix開源的動態屬性配置框架,基於apache commons configuration, 提供在執行時獲取配置值的功能。
-
Archaius的核心是可以容納一個或多個配置的複合配置的概念。每個配置都可以從諸如JDBC、REST介面、xxx.properties檔案等配置源中獲取。可以選擇在執行時對配置源進行輪詢以進行動態更改,
-
屬性的最終值取決於包含該屬性的最頂層配置(因為是複合配置)。即,如果一個屬性存在於多個配置中,則應用程式看到的實際值將是配置層次結構中最頂層插槽中的值,當然這種層次結構是可以配置的。
Archaius的架構設計
Archaius實際上是對Apache Common Configuration Library的一個封裝和擴充套件,提供了一組基於Java的配置API,主要的特性包括:
- 配置可動態調整:動態、型別屬性
- 配置支援型別(Int, Long, Boolean等)。
- 高效能和執行緒安全:高吞吐量和執行緒安全的配置操作
- 提供一個拉(pulling)配置的框架,可以從配置源動態拉取變更的配置。(一個輪詢框架,允許使用者獲取對配置源的屬性更改)
- 支援回撥(callback)機制,在配置變更時自動呼叫。
- 支援JMX MBean,可以通過JConsole檢視配置和修改配置。
對於願意使用基於約定的屬性檔案位置的應用程式(以及大多數web應用程式),提供開箱即用的複合配置(這是強大功能之一),對於符合配置官網給了一副示例圖如下:
Achaius的核心是一個稱為組合配置(Composite Configuration)的概念,簡單可以理解為一個分層級的配置,層級有優先順序,高優先順序的層級的配置會覆蓋低優先順序的配置。每一個層級可以從某個配置源獲取配置,例如本地配置檔案,JDBC資料來源,遠端REST API等。配置源還可以在執行時動態拉取變更,例如在上圖中,持久化資料庫配置(Persisted DB Configuration)是指將配置存在關聯式資料庫中,相應的配置源會定期從資料庫拉取變更)。配置的最終值由頂級配置決定,例如,如果多個層級都含有某個配置項,那麼應用最終見到的值是配置層級中最頂層的值。配置分層的順序是可以調整的。
通過archaius獲取配置值,有兩種方式:
- 一種是通過ConfigurationManager獲取到配置中心例項,然後通過propName獲取配置值,通過ConfigurationManager獲取配置
- 另外一種方式是通過DynamicPropertyFactory,獲取配置項的DynamicProperty wrapper。
Archaius的實現原理
Archaius是什麼?
Archaius提供了動態修改配置的值的功能,在修改配置後,不需要重啟應用服務。其核心思想就是輪詢配置源,每一次迭代,檢測配置是否更改,有更改重新更新配置。
底層archaius提供實現了Apache-common-configuration的AbstractConfiguration的具體實現。
-
ConcurrentMapConfiguration提供將配置項配置值放在ConcurrentHashMap中維護的功能。
-
DynamicConfiguration提供動態從資料來源獲取所有配置值的功能,通過輪詢資料來源更新配置值。
-
DynamicWatchedConfiguration也是提供動態更新配置的功能,與DynamicConfiguration不同的是,配置更新是資料來源有變化時觸發的。
-
DynamicConfiguration是pull方式,DynamicWatchedConfiguration是push方式。
ConcurrentCompositeConfiguration使用了組合模式,組合不同的AbstractConfiguration實現。對於有多個配置源的配置中心,可以使用ConcurrentCompositeConfiguration。對於同一個配置項,多個配置源都有配置值的時候,取第一個匹配到的配置源的資料。
DynamicPropertyFactory是怎麼執行的?
-
DynamicPropertyFactory持有AbstractConfiguration例項。
-
建立DynamicProperty物件時,DynamicProperty物件會獲取DynamicPropertyFactory持有的AbstractConfiguration例項。
-
DynamicProperty物件會向AbstractConfiguration例項註冊DynamicPropertyListener, 當AbstractConfiguration有增刪改查變化時,會通知到當前的DynamicProperty物件。
當建立的DynamicProperty例項數量比較大的時候,這裡可能有效能問題。每建立一個任何一個DynamicProperty,都會增加一個listener,同時,任何一個配置項發生變化,都會觸發listener。
可能是考慮到生產環境中不會有那麼多的配置項變更吧。像zk-config那種對應配置項變更才觸發watcher要好一點。
AbstractConfiguration是怎麼注入的?
archaius僅允許一個AbstractConfiguration的實現類。如果有多個配置源,可以使用上面提到的ConcurrentCompositeConfiguration將不同的AbstractConfiguration組合起來。
有以下幾種方式注入AbstractConfiguration。
-
配置archaius.default.configuration.class 指定AbstractConfiguration實現類
-
配置 archaius.default.configuration.factory 指定AbstractConfiguration例項工廠方法類,工廠方法類需要實現getInstance方法返回AbstractConfiguration例項
-
呼叫 ConfigurationManager 的 install(AbstractConfiguration config) 方法
-
呼叫DynamicPropertyFactory 的 initWithConfigurationSource(AbstractConfiguration config) 方法
以上方法是互斥的,只能使用其中的一種,一種生效後,其他的就不能呼叫了。
一個簡單的例子:
Maven依賴配置
<dependency>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-core</artifactId>
<version>0.7.7</version>
</dependency>
獲取配置源
/**
* Created by longfei on 17/1/19.
*/
public class DynamicConfigurationSource implements PolledConfigurationSource {
@Override
public PollResult poll(boolean initial,Object checkPoint) throws Exception {
Map<String,Object> map = new HashMap<>();
map.put("test",UUID.randomUUID().toString());
return PollResult.createFull(map);
}
}
定義排程器
AbstractPollingScheduler scheduler = new FixedDelayPollingScheduler(2000,2000,false);
定義動態配置
DynamicConfiguration configuration = new DynamicConfiguration(source,scheduler);
簡單單元測試
@org.testng.annotations.Test
public void testArchaius() throws Exception {
PolledConfigurationSource source = new DynamicConfigurationSource();
AbstractPollingScheduler scheduler = new FixedDelayPollingScheduler(2000,2000,false);
DynamicConfiguration configuration = new DynamicConfiguration(source,scheduler);
ConfigurationManager.install(configuration);
final DynamicStringProperty stringProperty = DynamicPropertyFactory.getInstance().getStringProperty("test","nodata");
Helpers.subscribePrint(Observable.interval(1,TimeUnit.SECONDS).take(20).doOnNext(new Action1<Long>() {
@Override
public void call(Long aLong) {
System.out.println(stringProperty.get());
}
}),"test");
TimeUnit.MINUTES.sleep(1);
}
實現
啟動輪詢任務
public synchronized void startPolling(PolledConfigurationSource source, AbstractPollingScheduler scheduler) {
this.scheduler = scheduler;
this.source = source;
init(source, scheduler);
scheduler.startPolling(source, this);
}
輪詢的Runnable和初始化:實現是一致的
PollResult result = null;
try {
result = source.poll(false,getNextCheckPoint(checkPoint));
checkPoint = result.getCheckPoint();
fireEvent(EventType.POLL_SUCCESS, result, null);
} catch (Throwable e) {
log.error("Error getting result from polling source", e);
fireEvent(EventType.POLL_FAILURE, null, e);
return;
}
try {
populateProperties(result, config);
} catch (Throwable e) {
log.error("Error occured applying properties", e);
}
注意到,會呼叫source.poll方法,即PolledConfigurationSource的polled,我們實現的資料來源介面,可以自定義資料來源(jdbc,檔案,scm等)
總結
在深入理解Archaius過程中,有一個繞不開的“障礙”便是Apache Commons Configuration,由於前者強依賴於後者進行配置管理。正所謂你對Apache Commons Configuration有多瞭解,決定了你對Netflix Archaius的認識有多深。