intro
archaius是Netflix oss(open source software)的配置核心。它有很強大的功能,可以讀配置文件的配置,系統的配置,還有zookeeper的配置等等的。
archaius原意是一種變色龍,那麼含義就是根據環境的不同可以改變配置。
我們會講一下它的基本用法和各個配置的覆蓋。
pom
各種依賴衝突把我折騰的都快放棄了,最後調出來的可用版本爲:
<dependencies>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-core</artifactId>
<version>0.7.4</version>
</dependency>
<dependency>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-zookeeper</artifactId>
<version>0.7.4</version>
</dependency>
<dependency>
<groupId>com.netflix.curator</groupId>
<artifactId>curator-test</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
basics
一個掌管配置的框架,一定能讀resources下的配置文件。
先建一個config.properties
,archaius找resources下的配置文件時,默認找的名字就是config.properties
。
stringprop=propvalue
listprop=value1, value2, value3
mapprop=key1=value1, key2=value2
longprop=100
我們存放普通的string值,list,map,還有long值,看它是不是都能讀。
@Test
public void testBasicStringProps() throws InterruptedException {
DynamicStringProperty sampleProp = DynamicPropertyFactory.getInstance().getStringProperty("stringprop", "");
System.out.println(sampleProp.get());
}
取出stringprop
對應的value,如果沒有的話就是空字符串。
DynamicPropertyFactory.getInstance()
是一個double check的單例模式:
public static DynamicPropertyFactory getInstance() {
if (config == null) {
synchronized (ConfigurationManager.class) {
if (config == null) {
AbstractConfiguration configFromManager = ConfigurationManager.getConfigInstance();
if (configFromManager != null) {
initWithConfigurationSource(configFromManager);
initializedWithDefaultConfig = !ConfigurationManager.isConfigurationInstalled();
logger.info("DynamicPropertyFactory is initialized with configuration sources: " + configFromManager);
}
}
}
}
return instance;
}
@Test
public void testBasicListProps() {
DynamicStringListProperty listProperty = new DynamicStringListProperty("listprop", Collections.<String>emptyList());
listProperty.get().stream().forEach(System.out::println);
}
取出list的值。
@Test
public void testBasicMapProps() {
DynamicStringMapProperty mapProperty = new DynamicStringMapProperty("mapprop", Collections.emptyMap());
Map<String, String> map = mapProperty.getMap();
map.forEach((k, v) -> System.out.println(k + "\t" + v));
}
取出map的值。
@Test
public void testBasicLongProperty() {
DynamicLongProperty longProp = DynamicPropertyFactory.getInstance().getLongProperty("longprop", 1000);
System.out.println(longProp.get());
}
取出long值。默認是1000,但是我們在配置文件中配了,所以結果是100。
config.properties override
如果我的配置文件不叫config.properties
,比如叫newconfig.properties
呢?
我們在newconfig.properties
中寫:
newstringprop=newstringvalue
如何把它加入進來呢?
@Before
public void setup() throws IOException {
ConfigurationManager.loadCascadedPropertiesFromResources("newconfig");
//System.setProperty("archaius.configurationSource.defaultFileName", "newconfig.properties");
}
@Test
public void testBasicStringProps() throws InterruptedException {
DynamicStringProperty sampleProp = DynamicPropertyFactory.getInstance().getStringProperty("newstringprop", "");
System.out.println(sampleProp.get());
}
這樣子就可以取出newstringprop
。
另一方面,config.properties
中的值照舊有效。
如果你使用System.setProperty()
的方式:
@Before
public void setup() throws IOException {
//ConfigurationManager.loadCascadedPropertiesFromResources("newconfig");
System.setProperty("archaius.configurationSource.defaultFileName", "newconfig.properties");
}
那就把newconfig.properties
當作默認配置文件了。這時config.properties
就失效了。
我們也可以進行配置文件的關聯動態地改值。比如對於環境的修改:
在sample.properties
中:
sampleprop=samplevalue
@next=sample-$(@environment).properties
現在我這個sampleprop
的值要去sample-xxx.properties
的文件中找,@environment
需要我們動態地給值。
比如最終找的是sample-test.properties
:
sampleprop=propvalue-test
測試:
public class TestArchaiusOverriding {
@Before
public void setUp() throws IOException {
ConfigurationManager.getConfigInstance().setProperty("@environment", "test");
ConfigurationManager.loadCascadedPropertiesFromResources("sample");
}
@Test
public void testOverringEnvironment() {
DynamicStringProperty sampleProperty = DynamicPropertyFactory.getInstance().getStringProperty("sampleprop", "");
System.out.println(sampleProperty.get());
}
}
more configurations and overriding
配置當然還有很多。在接下來的測試中,我們會使用zookeeper配置,自定義的map配置,和系統配置。
啓動zookeeper,建立/config
節點。
//zookeeper的根路徑,在自己的zookeeper中建好這個節點
private static final String CONFIG_ROOT_PATH = "/config";
//客戶端
private static CuratorFramework client;
//zookeeper的配置資源類
private static ZooKeeperConfigurationSource zkConfigSource;
//自定義的配置類,就是一個map
private static ConcurrentMapConfiguration mapConfig;
private static final Charset charset = Charset.forName("UTF-8");
@BeforeClass
public static void setUp() throws Exception {
//連上本地的zookeeper
client = CuratorFrameworkFactory.newClient("localhost:2181", new ExponentialBackoffRetry(1000, 3));
client.start();
//本地緩存
zkConfigSource = new ZooKeeperConfigurationSource(client, CONFIG_ROOT_PATH);
zkConfigSource.start();
// 設置系統值
System.setProperty("test.key4", "test.value4-system");
System.setProperty("test.key5", "test.value5-system");
final ConcurrentMapConfiguration systemConfig = new ConcurrentMapConfiguration();
//加載系統值到一個map
systemConfig.loadProperties(System.getProperties());
//動態監視zookeeper配置的變化
final DynamicWatchedConfiguration zkDynamicOverrideConfig = new DynamicWatchedConfiguration(zkConfigSource);
//自定義map配置
mapConfig = new ConcurrentMapConfiguration();
mapConfig.addProperty("test.key1", "test.value1-map");
mapConfig.addProperty("test.key2", "test.value2-map");
mapConfig.addProperty("test.key3", "test.value3-map");
mapConfig.addProperty("test.key4", "test.value4-map");
//把三種配置合起來
final ConcurrentCompositeConfiguration compositeConfig = new ConcurrentCompositeConfiguration();
compositeConfig.addConfiguration(zkDynamicOverrideConfig, "zk dynamic override configuration");
compositeConfig.addConfiguration(mapConfig, "map configuration");
compositeConfig.addConfiguration(systemConfig, "system configuration");
// 設置zookeeper的配置
setZkProperty("test.key1", "test.value1-zk");
setZkProperty("test.key2", "test.value2-zk");
setZkProperty("test.key4", "test.value4-zk");
//加載配置
ConfigurationManager.install(compositeConfig);
}
這三種配置,有一些key是重合的。
比如mapConfig
的test.key1
、test.key2
、test.key4
就與zookeeper一樣,但是值是不同的。
然後我們這裏使用的是zookeeper的客戶端框架curator。
private static void setZkProperty(String key, String value) throws Exception {
final String path = CONFIG_ROOT_PATH + "/" + key;
byte[] data = value.getBytes(charset);
//查看key是否原來存在,有就更新,沒有就建
try {
// attempt to create (intentionally doing this instead of checkExists())
client.create().creatingParentsIfNeeded().forPath(path, data);
} catch (KeeperException.NodeExistsException exc) {
// key already exists - update the data instead
client.setData().forPath(path, data);
}
}
這個方法通過zookeeper的client來創建或者更新配置中的值。
//關閉配置資源
@AfterClass
public static void tearDown() throws Exception {
zkConfigSource.close();
}
最後還要關一下ZooKeeperConfigurationSource
。
開始測試:
@Test
public void testZkPropertyOverride() throws Exception {
setZkProperty("test.key1", "test.value1-zk");
// there is an override from ZK, so make sure the overridden value is being returned
System.out.println(DynamicPropertyFactory.getInstance().getStringProperty("test.key1", "default").get());
}
zookeeper覆蓋mapConfig。
@Test
public void testNoZkPropertyOverride() throws Exception {
// there's no override, so the map config value should be returned
System.out.println(DynamicPropertyFactory.getInstance().getStringProperty("test.key3", "default")
.get());
}
沒被zookeeper覆蓋,返回mapConfig中的值。
@Test
public void testDefault() throws Exception {
// there's no property set, so the default should be returned
System.out.println(DynamicPropertyFactory.getInstance().getStringProperty("test.key99", "default")
.get());
}
沒有test.key99
這個key,返回default。
@Test
public void testSystemPropertyOverride() throws Exception {
// there's a system property set, but this should not trump the zk override
System.out.println(DynamicPropertyFactory.getInstance().getStringProperty("test.key4", "default")
.get());
// there's a system property set, but no other overrides, so should return the system property
System.out.println(DynamicPropertyFactory.getInstance().getStringProperty("test.key5", "default")
.get());
}
systemConfig
、mapConfig
和zookeeper中都有test.key4
,最終勝出的是zookeeper。
test.key5
是系統屬性獨有的。
@Test
public void testUpdatePropertyOverride() throws Exception {
setZkProperty("test.key1", "test.value1-zk");
// update the map config's property and assert that the value is still the overridden value
mapConfig.setProperty("test.key1", "prop1");
System.out.println(DynamicPropertyFactory.getInstance()
.getStringProperty("test.key1", "default").get());
}
mapConfig
試圖修改test.key1
的值,不過那是不會成功的。
@Test
public void testUpdateZkProperty() throws Exception {
setZkProperty("test.key1", "test.value1-zk-override");
System.out.println(DynamicPropertyFactory.getInstance()
.getStringProperty("test.key1", "default").get());
}
zookeeper一動手,就能改成功。