前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[享学Netflix] 十二、Archaius动态属性DynamicProperty原理详解(重要)

[享学Netflix] 十二、Archaius动态属性DynamicProperty原理详解(重要)

作者头像
YourBatman
发布于 2020-03-18 11:29:02
发布于 2020-03-18 11:29:02
4.3K00
代码可运行
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦
运行总次数:0
代码可运行

要相信:使用这些类库,你的代码将变得更好。 代码下载地址:https://github.com/f641385712/netflix-learning

目录
  • 前言
  • 正文
    • DynamicProperty
      • DynamicPropertyListener
      • 使用示例
    • DynamicPropertyFactory
      • 使用示例
  • 总结
    • 声明

前言

上篇文章了解到了Netflix Archaius它提供的两个支持类:配置管理器ConfigurationManager和动态属性支持DynamicPropertySupport。特别是DynamicPropertySupport它提供了对动态属性的支持,原理便是通过PropertyListener来完成。

本文将深入了解Netflix Archaius动态属性相关内容,它用DynamicProperty表示一个动态k-v属性值,再结合DynamicPropertySupport提供的监听支持,最终实现属性动态化。


正文

DynamicProperty:动态属性,一个实例对象代表一个k-v,然后具有动态的能力 DynamicPropertyFactory:用于构建一个动态属性实例,屏蔽掉DynamicProperty具体实现,对使用者友好。


DynamicProperty

一个带有缓存的动态配置属性,它具有动态性:当属性变更时会自动更新其缓存。该对象是线程安全的,并且访问速度非常快,高于System.getProperty()

说明:效率高于System得益于ConcurrentHashMap的效率高于Properties(Hashtable)

使用场景:该类用于多次获取属性值,并且该值可能会动态更改的情况。如果属性只读一次,则应使用“普通”访问方法。如果属性值是固定的,请考虑将该值缓存在变量中(用变量表示)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DynamicProperty {
	private volatile static DynamicPropertySupport dynamicPropertySupportImpl;

	// 这就是它速度快的原因。注意是static的哟,需要放置内存泄漏
	private static final ConcurrentHashMap<String, DynamicProperty> ALL_PROPS = new ConcurrentHashMap<>();

	...
    private String propName; // 属性名
    private String stringValue = null; // 属性值
    private long changedTime;
    private CopyOnWriteArraySet<Runnable> callbacks = new CopyOnWriteArraySet<>();
    ...

	// 重要抽象:特定类型的缓存Value
	// 它的特点是速度快,且支持变化后立马自更新
	private abstract class CachedValue<T> {

		// 这三个属性均是volatile的
        private volatile boolean isCached;
        private volatile IllegalArgumentException exception;
        private volatile T value;

		// 获取值方法:对外public
		public T getValue() throws IllegalArgumentException {
			// 若还没缓存过,那就加锁去缓存
			if (!isCached) {
				synchronized (lock) {
					...
					//缓存的时候有个parse(stringValue);数据转换动作
					// 当然也有可能抛出异常
					parse(stringValue);
					... 
				}
			} else {
				... // 返回缓存的值:成员属性value值
			}
			
		}
		public T getValue(T defaultValue) { ... }
		// 数据转换的抽象方法,子类自行实现
		protected abstract T parse(String rep) throws Exception;
	}
	
	// 请记住这两个数组:很多简写都表示true哦,这基本是业界规范
    private static final String[] TRUE_VALUES =  { "true",  "t", "yes", "y", "on"  };
    private static final String[] FALSE_VALUES = { "false", "f", "no",  "n", "off" };
    private CachedValue<Boolean> booleanValue = rep -> ...;
    private CachedValue<Long> longValue = rep -> Long.valueOf(rep);
    private CachedValue<String> cachedStringValue = ...
    ... 
    private CachedValue<Class> classValue= rep -> Class.forName(rep);
    
    // 有了这种缓存值,下面获取就方便了
    public String getString() {
        return cachedStringValue.getValue();
    }
    ...
    public Class getNamedClass() throws IllegalArgumentException {
        return classValue.getValue();
    }
    public <T> Optional<T> getCachedValue(Class<T> objectType) { ... }


	// 单例设计:获取一个DynamicProperty 实例,才能拿到具体的value值嘛
	// 说明:这个过程的线程安全性是由`ConcurrentHashMap`去保证的
    public static DynamicProperty getInstance(String propName) {
		// 空调用:确保配置源Configuration已经注册上了DynamicPropertyListener监听
        if (dynamicPropertySupportImpl == null) {
            DynamicPropertyFactory.getInstance();
        }	
	
		// 先从缓存拿,拿不到就临死构造一个放进缓存里
        DynamicProperty prop = ALL_PROPS.get(propName);
        if (prop == null) {
            prop = new DynamicProperty(propName);
            DynamicProperty oldProp = ALL_PROPS.putIfAbsent(propName, prop);
            if (oldProp != null) {
                prop = oldProp;
            }
        }
        return prop;
    }


    private DynamicProperty(String propName) {
        this.propName = propName;
        updateValue();
    }
}

能被缓存的值只有如上类型,当然String类型可以代表任何值,所以它还是通用的。但你是否发现,到目前而止你还不知道如何初始化一个DynamicProperty,也就是给其属性如:propName/stringValue等等赋值,下面就介绍下它的初始化相关相关方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DynamicProperty:

	// 唯一初始化方法:初始化一个DynamicProperty并且给其配置好DynamicPropertyListener监听器
	// 从而具有缓存+动态的功能。初始化的时候会updateAllProperties()
	// 特点:此方法非public
    static synchronized void initialize(DynamicPropertySupport config) {
        dynamicPropertySupportImpl = config;
        config.addConfigurationListener(new DynamicPropertyListener());
        updateAllProperties();
    }
    // 该方法含义同上
    static void registerWithDynamicPropertySupport(DynamicPropertySupport config) {
        initialize(config);
    }


    // 返回值true:代表值有变化(只要有一个有变换就返回true)
    private static boolean updateAllProperties() {
        boolean changed = false;
        for (DynamicProperty prop : ALL_PROPS.values()) {

			// 更新值来自于:dynamicPropertySupportImpl.getString(propName);
			// 其实也就是Configuration文件里喽
            if (prop.updateValue()) {
                prop.notifyCallbacks(); // 若有变化,就触发绑定在该值上的所有回调
                changed = true;
            }
        }
        return changed;
    }

这样子就完成了整个属性值的初始化:把Configuration里面所有的实现都初始化为DynamicProperty然后缓存在全局static变量的Map里面,这样子访问速度变快了并且还天然具有动态的能力了。

但是,initialize方法它并非public的,它唯一被(间接)调用处是在DynamicPropertyFactory#initWithConfigurationSource()这个public方法里,这个类文下也会有详解。


DynamicPropertyListener

它是DynamicProperty的一个静态内部类,用于监听 DynamicProperty持有的DynamicPropertySupport,从而可以完成熟悉发生改变时(新增、修改、clear等),执行对应的操作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DynamicProperty:

	// 初始化DynamicProperty时候完成监听器的注册
    static synchronized void initialize(DynamicPropertySupport config) {
        dynamicPropertySupportImpl = config;
        config.addConfigurationListener(new DynamicPropertyListener());
        updateAllProperties();
    }
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static class DynamicPropertyListener implements PropertyListener {

		// source刚被加载,就可以update所有
        @Override
        public void configSourceLoaded(Object source) {
            updateAllProperties();
        }

		// 添加属性时,做相应处理
        @Override
        public void addProperty(Object source, String name, Object value, boolean beforeUpdate) {
        	// 成功后
            if (!beforeUpdate) {
                updateProperty(name, value);
            } else { // 成功前,先校验
                validate(name, value);
            }
        }
        // 修改一个值时,逻辑完全同上
        public void setProperty( ... ){ ... }
        // 成功后,updateProperty(name, value);即可
        public void clearProperty( ... ){ ... }

		// 清空所有属性时候触发的动作(成功后)
        @Override
        public void clear(Object source, boolean beforeUpdate) {
            if (!beforeUpdate) {
                updateAllProperties();
            }
        }
}

DynamicProperty的动态性是通过此监听器器来实现的:当org.apache.commons.configuration.Configuration属性发生变化时,该监听器对应方法就会被触发。


使用示例

DynamicProperty并不能直接构建,因此它的使用示例请参见DynamicPropertyFactory


DynamicPropertyFactory

顾名思义,该类表示DynamicProperty动态属性的工厂。创建动态属性实例并将其与底层配置或动态属性支持关联的工厂,在运行时可以动态更改这些属性

该工厂以单例形式使用,单例的初始化逻辑还蛮有看头的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DynamicPropertyFactory {

	private static DynamicPropertyFactory instance = new DynamicPropertyFactory();
    private DynamicPropertyFactory() {}

	// 依赖于下面方法,这是内部帮你把AbstractConfiguration适配为了DynamicPropertySupport
	public static DynamicPropertyFactory initWithConfigurationSource(AbstractConfiguration config) { ... }
	// 这个方法也是返回一个工厂的实例:但它要求你必须传入你的DynamicPropertySupport实现类喽(而不能用系统默认的)
    public static DynamicPropertyFactory initWithConfigurationSource(DynamicPropertySupport dynamicPropertySupport) {
        synchronized (ConfigurationManager.class) {
            if (dynamicPropertySupport == null) { // 必传
                throw new IllegalArgumentException("dynamicPropertySupport is null");
            }

			// 很明显:这里大概率是ConfigurationBackedDynamicPropertySupportImpl
            AbstractConfiguration configuration = null;
            if (dynamicPropertySupport instanceof AbstractConfiguration) {
                configuration = (AbstractConfiguration) dynamicPropertySupport;
            } else if (dynamicPropertySupport instanceof ConfigurationBackedDynamicPropertySupportImpl) {
                configuration = ((ConfigurationBackedDynamicPropertySupportImpl) dynamicPropertySupport).getConfiguration();
            }
			... // 忽略部分校验
           
			// 这个setDirect有意思了:就是给ConfigurationManager管理的直接设置一些值
			// 值来源于:AbstractConfiguration,比如instance、监听器等等
            if (configuration != null && configuration != ConfigurationManager.instance) {
                ConfigurationManager.setDirect(configuration);
            }
            // 给本类本厂的本类的属性赋值
            setDirect(dynamicPropertySupport);
            return instance;
        }
    }
    static void setDirect(DynamicPropertySupport support) {
        synchronized (ConfigurationManager.class) {
            config = support;
            // 这就是对DynamicProperty进行init初始化动作喽
            // configuration里面所有的属性都会变为动态的,然后缓存着
            DynamicProperty.registerWithDynamicPropertySupport(support);
            initializedWithDefaultConfig = false;
        }
    }


	
	// 一般我们会使用它:因为它默认帮我们构建new ConfigurationBackedDynamicPropertySupportImpl(config)作为实现
	// 不用操心。而使用者只需关心Configuration配置源即可
	// 也就是ConfigurationManager.getConfigInstance()这个逻辑
    public static DynamicPropertyFactory getInstance() {
        ... // 逻辑同上的initWithConfigurationSource
        return instance;
    }
}

该工厂能够屏蔽DynamicProperty的初始化以及使用逻辑,让调用者只需关心配置源Configuration即可。当然,它还有一些其它帮助方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DynamicPropertyFactory:

	// 这个值:只有Configuration是根据你配置的系统属性来初始化的才是true
	private volatile static boolean initializedWithDefaultConfig = false;
	
	// 默认情况下:是不会开启JMX支持的哦
	public static final String ENABLE_JMX = "archaius.dynamicPropertyFactory.registerConfigWithJMX";

	// 获取支持的配置源
	// 你可以基于Apache Commons Configuration实现,但不是必须的。所以这里返回的是Object
    public static Object getBackingConfigurationSource() {
        if (config instanceof ConfigurationBackedDynamicPropertySupportImpl) {
            return ((ConfigurationBackedDynamicPropertySupportImpl) config).getConfiguration();
        } else {
            return config;
        }
    }

	// 返回一个DynamicStringProperty,内部原理实际为DynamicProperty
    public DynamicStringProperty getStringProperty(String propName, String defaultValue) {
        return getStringProperty(propName, defaultValue, null);
    }
    public DynamicStringProperty getStringProperty(String propName, String defaultValue, final Runnable propertyChangeCallback) {
        checkAndWarn(propName);
        DynamicStringProperty property = new DynamicStringProperty(propName, defaultValue);
        addCallback(propertyChangeCallback, property);
        return property;
    }
    ... // 省略getInt、getLong、getDouble等等
    
    // 获取一个带上下文的动态属性
    public <T> DynamicContextualProperty<T> getContextualProperty(String propName, T defaultValue) { ... }

DynamicPropertyFactory的设计可以看到,它是面向使用者的API,希望屏蔽掉DynamicProperty的实现细节,使得对调用者更加友好。


使用示例
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Test
public void fun2() throws InterruptedException {
    DynamicPropertyFactory propertyFactory = DynamicPropertyFactory.getInstance();
    DynamicStringProperty nameProperty = propertyFactory.getStringProperty("name", "defaultName");
    nameProperty.addCallback(() -> System.out.println("name属性值发生变化:"));

    // 10秒钟读一次
    while (true) {
        System.out.println(nameProperty.get());
        TimeUnit.SECONDS.sleep(50);
    }
}

运行程序控制台输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
YourBatman
name属性值发生变化:
YourBatman-changed

这就是动态属性的特点:你只需要更改你配置文件的值即可。

注意:DynamicProperty默认是1分钟重载一次哦,具体可参照DynamicURLConfiguration这个类

和System性能比较: 其实就是HashtableConcurrentHashMap + cache的性能比较,很显然后者占优,有兴趣者可自行尝试。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
重定向、别名、绝对路径、相对路径 详解
惨绿少年
2017/12/28
1.5K0
Python代码中,该用绝对路径还是相对路径?
还记得那年,我在一个小型创业公司当实习生。那天晚上,我和同事小李正在赶一个紧急项目,凌晨两点的办公室,只有键盘的敲击声和窗外微弱的路灯。代码写得差不多了,我们满怀信心地运行,结果却报了个 FileNotFoundError。
renhai
2024/10/21
1890
Python代码中,该用绝对路径还是相对路径?
Linux的相对路径和绝对路径、cd命令、创建和删除目录、rm命令
相对路径就是相对当前所在目录来说的路径,绝对路径就是相对/(根)目录来说的路径。相对路径不以/开头,绝对路径以/开头,./abc是相对路径(./表示当前路径),/abc是绝对路径。但是:~/abcd/123/是一个绝对路径,因为~符号表示的是用户的家目录,所以这个是绝对路径。
端碗吹水
2020/09/23
8.3K0
Linux的相对路径和绝对路径、cd命令、创建和删除目录、rm命令
struts/Servlet,action转到jsp后,路径问题(struts2,jsp路径,action路径,action跳转,相对路径,绝对路径)
问题:使用struts2,如何处理action的路径?还有,在action转到的jsp中,如何写js,css,图 片的路径?(例如访问 http://localhost/project/listUser.action后转到http://localhost/project/user /listUser.jsp,这时候浏览器还是停留在http://localhost/project/addUser.action。这时候jsp原来的图片定位可能就都出错了) 答:action的话,就不需要关心路径问题。我们不
用户1258909
2018/07/03
3K0
【软考学习14】绝对路径和相对路径的区别和联系
比如我在 D 盘的某个文件夹下新建了一个 helloWorld.java 文件,如下图所示。
Designer 小郑
2023/08/01
8610
【软考学习14】绝对路径和相对路径的区别和联系
python 学习笔记(8)——python绝对路径相对路径
今天在调试代码的时候,程序一直提示没有该模块,一直很纳闷,因为我导入文件一直是用绝对路径进行导入的。按道理来讲是不会出现模块找不到的情况的。    最后仔细分析了整个代码的目录结构,才发现了问题。
my_sunshine
2020/09/18
5.6K0
Python绝对路径和相对路径详解
每个运行在计算机上的程序,都有一个“当前工作目录”(或 cwd)。所有没有从根文件夹开始的文件名或路径,都假定在当前工作目录下。
用户8442333
2021/11/30
3.5K0
【HTML】HTML 标签 ④ ( 文件路径 | 相对路径 | 绝对路径 | 同级目录 | 下一级目录 | 上一级目录 )
一个前端项目 , 可能有几百上千个 html , css , javascript 文件 , 这些文件都需要通过不同的目录层级进行整理存放 ;
韩曙亮
2023/03/30
1.9K0
【HTML】HTML 标签 ④ ( 文件路径 | 相对路径 | 绝对路径 | 同级目录 | 下一级目录 | 上一级目录 )
Linux基础命令——绝对路径和相对路径
1. 绝对路径 从根目录算起的路径叫做绝对路径 例如: /home/python/Desktop /usr/bin 2. 相对路径 从当前目录算起的路径叫做相对路径 例如: ./test/hello ../static/images 3. 绝对路径和相对路径的使用 使用绝对路径切换到桌面 cd /home/python/Desktop 在下载目录以相对路径切换到桌面 cd Downloads cd ../Desktop 在桌面使用相对路径切换到上一级目录的上一级目录 cd Desktop cd ../.
落雨
2022/03/08
33.9K0
相对路径和绝对路径的区别
在HTML里只要涉及文件的地方(如超级链接、图片等)就会涉及绝对路径与相对路径的概念。  1.绝对路径     绝对路径是指文件在硬盘上真正存在的路径。例如“bg.jpg”这个图片是存放在硬盘的“E:\book\网页布局代码\第2章”目录下,那么 “bg.jpg”这个图片的绝对路径就是“E:\book\网页布\代码\第2章\bg.jpg"。那么如果要使用绝对路径指定网页的背景图片就应该使用 以下语句: <body backround="E:\book\网页布局\代码\第2章\bg.jpg" >    2.使
猿人谷
2018/01/17
2.7K0
web项目中,视图层中关于相对路径和绝对路径
1.在jfinal项目中   因为一直使用的jfinal,没感觉路径问题。   举个栗子,项目名字叫做test.访问一个Controller的映射为/user/add.这样,在浏览器地址栏直接:localhost:8080/user/add就可以直接访问到add方法了。当然,这样需要通过配置不同的端口来发布不同的项目,不然肯定冲突了。端口指定项目的路径。 比如: <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="t
Ryan-Miao
2018/03/13
1.8K0
Linux: Shell脚本中处理文件路径参数,兼容相对路径与绝对路径
在编写Shell脚本时,处理文件路径参数是一个常见的需求。路径参数可能是相对路径或绝对路径,如何编写一个兼容两者的Shell脚本并进行路径检查,是本文的主要内容。
运维开发王义杰
2024/06/11
9480
Linux: Shell脚本中处理文件路径参数,兼容相对路径与绝对路径
Python文件操作—— 补充(“相对路径”与“绝对路径”的区别)
我们在进行文件操作的时候,难免会遇到路径问题,路径分为两种:绝对路径和相对路径,所谓路径就是你要访问的对象的位置,下面我就来给大家介绍一下这两者的区别
Gorit
2021/12/09
1.6K0
Python文件操作—— 补充(“相对路径”与“绝对路径”的区别)
为什么 Vite 的请求有时候是相对路径,有时候是 /@fs/ + 绝对路径?
在开发项目时,我发现有时候请求资源的路径是相对路径,有时候是 /@fs/ 开头的绝对路径,这是为什么呢?
CandyTong
2023/01/05
4K0
Linux 中的绝对路径与相对路径:有什么区别?
路径是您引用文件和目录的方式,它给出了文件或目录在 Linux 目录结构中的位置,它由名称和斜杠语法组成。
网络技术联盟站
2022/04/20
2.8K0
Linux 中的绝对路径与相对路径:有什么区别?
(Java)路径问题(绝对路径、相对路径)
·以 “/” 开头的路径都是 绝对路径,不以当前文件的位置作为起始,而是以一个固定位置作为起始到达目标文件所经过的路径。
qubianzhong
2018/09/19
5.5K0
文件的相对路径和绝对路径以及根相对路径
  如果当前文档和目标文档所在的文件夹位置平行,则书写为:文件夹名称/目标文档全称;
阿豪聊干货
2018/08/09
2.9K0
在Servlet与JSP中取得当前文件所在的相对路径与绝对路径
<%@ page contentType="text/html;charset=GBK"%>
阿敏总司令
2019/02/28
1.3K0
绝对路径和相对路径(转)
1、相对路径-顾名思义,相对路径就是相对于当前文件的路径。网页中一般表示路径使用这个方法。
山河木马
2019/03/05
2.5K0
Pycharm相对路径
今天有个程序,明显路径是存在的,但是os.path.exists的返回结果是False. 仔细想了想, 是相对路径的问题.
狼啸风云
2020/12/17
3.2K0
Pycharm相对路径
推荐阅读
相关推荐
重定向、别名、绝对路径、相对路径 详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档