一、简介
众所周知,oslo_config是openstack框架下各服务加载配置文件(.conf)所使用的通用库,配置文件一般放在/etc目录的工程目录下,oslo_config一般是在服务启动时,提前加载配置数据到内存中,之后供服务使用;此处不啰嗦太多,主要还是探讨oslo_config cfg.py文件的设计优点。
二、cfg.py解析
class ConfigOpts(collections.Mapping):
def __init__(self):
pass # 初始化代码省略
...
def __clear_cache(f):
pass
def __call__(self, ...):
pass
def __getattr__(self, name):
pass
def __getitem__(self, key):
pass
def __iter__(self):
pass
@__clear_cache
def clear(self):
pass
CONF = ConfigOpts()
以上核心代码全部省,请查阅以上只分析这么设计的好处:
继承collections.Mapping,collections.Mapping为一个抽象map类,继承此类必须实现这几个抽象方法:__getitem__, __iter__, __len__,并且继承了dict一些通用方法,将ConfigOpts变成了一个字典类,可以像使用dict函数一样使用ConfigOpts创建的对象。
__init__中并未有传参来初始化变量,但是同步实现了__calll__,这么设计是为了之后使用单例设计模式埋下伏笔,程序的末尾CONF对象可以像函数一样调用初始化内部参数,只要在一处初始化之后,其余模块直接from cfg import CONF就可以直接使用这个实例对象,CONF这个实例对象就会一直加载在内存中,直到服务进程结束,很方便其余线程使用,并且不需要加线程锁来避免高并发引来的数据竞争问题 。
__getattr__重实现,可以将CONF实例获取key值时,直接使用CONF.key_name就可以获取,而不用使用CONF["key_name"],简化了数据获取操作;实际上通过CONF["key_name"]获取value值时,调用的__getitem__方法,而__getitem__方法内部函数调用__getattr__获取的值。
__iter__将CONF对象遍历时变成一个迭代器对象,有助于提升性能。
__clear_cache实现成为一个装饰器,在调用clear方式的时候使用;ConfigOpts的缓存是在get操作获取key的value值时使用,将每次获取的key与value值缓存到一个字典中,在之后的调用中先从字典中直接获取,此处代码未展示出来,想要看的可以看源码。
注意:
刚才说了,此种设计可以理解成一种伪单例设计模式,但是初始化CONF时必须保证是在服务刚启动,其余模块未加载CONF中的数值时进行
三、引发思考
oslo_config用来加载配置项,但是一般配置项在使用中途,都有可能更换配置项的参数值,但是更新之后,服务中加载的配置项还是旧的值,这如何处理?
思考:由于oslo_config加载的配置项数据在内存中,使用其余的服务修改conf文件内容,确实不好对内存中的数据做修改;此处oslo_config将配置项的数据直接加载在内存中供服务使用,我觉得是从两方面考量的,一个是一般配置项数据本身比较少,加载在内存中也占用不了太大空间;第二个是一般配置项数据,服务会频繁使用,放在存在中取数据速度会很快,提升性能;但是缺点就是如果配置项中的数据如果在服务运行途中发生变更,在不重启服务的前提下确实不好处理;
从性能方面考虑感觉可以有如下两种方案可以解决此问题:
修改配置项之后,立马重启服务,这我感觉是一种比较挫的方法
将修改后的配置项写入memcache或者redis中,每次从CONF中获取数值时,先校验redis中是否有值,如果Redis中有值的话,更新CONF中的值,将Redis中的值移除,我感觉这种方式可以解决此种问题,而且不用重启服务