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一动手,就能改成功。