Archaius 配置管理API,包含一系列配置管理API,提供動态類型化屬性、線程安全配置操作、輪詢架構、回調機制等功能。
概述
archaius是Netflix公司開源項目之一,基于java的配置管理類庫,主要用于多配置存儲的動态擷取。主要功能是對apache common configuration類庫的擴充。在雲平台開發中可以将其用作分布式配置管理依賴構件。同時,它有如下一些特性:
- 動态類型化屬性
- 高效和線程安全的配置操作
- 配置改變時的回調機制
- 輪詢架構
- JMX,通過Jconsole檢查和調用操作屬性
- 組合配置
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuUGM0EGZwMTY0UzN0QDZ4gTY4cTYyQmM0MGO4UmN5UmMfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
image.png
适用場景
對于傳統的單體應用,properties等配置檔案可以解決配置問題,同時也可以通過maven profile配置來差別各個環境,但在一個幾百上千節點的的微服務生态中,微服務采用多種語言開發,配置檔案格式多樣,如何把每個微服務的配置檔案都進行更新,并且很多時候還需要重新開機服務,是一件無法忍受的事情。是以,對于微服務架構而言,一個通用的配置中心是必不可少的。
新接口邏輯上線,老接口面臨遷移,開發測試完成後,馬上要上線。但是接口調用發的研發同學對新接口的穩定性、性能存在一定的質疑,為了避免風險,要求可以上線後緊急切換回老接口。這時候我們就需要一個手動開關。是以對于類似需求,一個通用的配置中心是必不可少的。
Archaius提供的DynamicIntProperty類可以在配置發生變化時動态地擷取配置,并且不需要重新開機應用,而底層的配置存儲,建議使用zookeeper進行存儲,Archaius作為用戶端的類庫使用。
代碼案例
引入依賴
<dependency>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-core</artifactId>
</dependency>
自定義Configuration
PropertiesConfiguration
public class PropertiesConfiguration extends DynamicConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesConfiguration.class);
private static final int INITIAL_DELAY_MILLIS = 0;
private static final int DELAY_MILLIS = 60 * 1000;
private static final boolean IGNORE_DELETES_FROM_SOURCE = true;
public PropertiesConfiguration(String confDir) {
this(new String[]{confDir});
}
public PropertiesConfiguration(final String...confDirs) {
String[] propertiesPaths = Lists.newArrayList(Iterables.concat(Iterables.transform(Arrays.asList(confDirs), new Function<String, List<String>>() {
@Nullable
@Override
public List<String> apply(String confDir) {
Assert.isTrue(new File(confDir).isDirectory(), StringUtil.format("路徑[{}]無法查找[.properties]檔案", confDirs));
String[] propertiesPaths = getPaths(confDir);
if (ArrayUtils.isNotEmpty(propertiesPaths)) {
return Lists.newArrayList(propertiesPaths);
} else {
return Lists.newArrayList();
}
}
}))).toArray(new String[0]);
if (ArrayUtils.isNotEmpty(propertiesPaths)) {
super.startPolling(new URLConfigurationSource(propertiesPaths), new FixedDelayPollingScheduler(INITIAL_DELAY_MILLIS, DELAY_MILLIS, IGNORE_DELETES_FROM_SOURCE));
}
ConfigurationLog.successInit(PropertiesConfiguration.class, this.getProperties());
}
private static String[] getPaths(String confDir) {
try {
URL configHome = new File(confDir).toURI().toURL();
List<String> urls = new ArrayList<String>();
for (String filename : FileUtil.scan(confDir, ".properties$")) {
String url = configHome.toString() + filename;
urls.add(url);
}
return urls.toArray(new String[urls.size()]);
} catch (MalformedURLException e) {
throw Throwables.propagate(e);
}
}
}
SystemConfiguration
public class SystemConfiguration extends ConcurrentMapConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(SystemConfiguration.class);
public SystemConfiguration() {
super();
this.loadProperties(System.getProperties());
ConfigurationLog.successInit(SystemConfiguration.class, this.getProperties());
}
}
同理,可以使用zookeeper client 封裝一個基于zookeeper的 ConcurrentMapConfiguration
初始化
private static final ConcurrentCompositeConfiguration compositeConfig = new ConcurrentCompositeConfiguration();
public synchronized static void init() {
Preconditions.checkState(! hadInit, StringUtil.format("[{}]隻能加載一次!", ConfigAdapter.class.getSimpleName()));
Preconditions.checkState(compositeConfig.getConfigurations().size() > 1,
StringUtil.format("[{}]沒有加載任何配置", ConfigAdapter.class.getSimpleName()));
if (! ConfigurationManager.isConfigurationInstalled()) {
ConfigurationManager.install(compositeConfig);
Preconditions.checkState(ConfigurationManager.isConfigurationInstalled(), StringUtil.format("[{}]加載失敗!",
ConfigAdapter.class.getSimpleName()));
}
Iterable<String> configurationNames = Iterables.transform(compositeConfig.getConfigurations(), new Function<AbstractConfiguration, String>() {
@Nullable
@Override
public String apply(AbstractConfiguration input) {
return input.getClass().getSimpleName();
}
});
ConfigurationLog.successInit(ConfigAdapter.class, getAll());
hadInit = true;
}
擷取值
public static DynamicBooleanProperty getDynamicBool(String key, boolean defaultValue) {
return getFactory().getBooleanProperty(key, defaultValue);
}
private static DynamicPropertyFactory getFactory() {
return DynamicPropertyFactory.getInstance();
}
注意
- 在設定的時刻擷取配置,配置源不會随着System#properties裡面的配置更新而更新
- 更新配置方法不會更新實際的property檔案,僅僅為更新記憶體資料,重新開機後失效
- 微服務都從配置中心動态的讀取配置資訊,而配置中心又在從配置源同步配置,是以這裡就很自然的出現了一個讀寫安全的問題,好消息是Archaius已經解決了這個問題,Archaius是線程安全的,讀寫可以并發進行。
個人介紹:
高廣超:多年一線網際網路研發與架構設計經驗,擅長設計與落地高可用、高性能網際網路架構。
本文首發在
高廣超的簡書部落格轉載請注明!