背景
最近在结合 python-3.12.0a6 版本开发一个多线程架构的后台服务;服务启动时会读取配置文件,并且要求所有线程共享同一份配置。
服务本身直接通过 http 接口来动态调整配置项的值,还要做到服务退出之后持久化配置项到配置文件。
一开始以为这个用 python 写也会要用几百行 ,最后发现完成核心功能就只需要不到 50 行,python 牛逼!!!
需求一:支持简单的配置项
假设我们目前只支持 name 和 port 两个配置项,多支持几个不难,只是不方便演示。
实例配置管理from dataclasses import dataclass@dataclassclass config(object): name:str= mysql port:int = 3306 看起来是没问题了,下面可以试用一下,也顺带引导出第二个需求。
in [6]: a = config()in [7]: b = config()in [8]: id(a)out[8]: 4407850896in [9]: id(b)out[9]: 4407852496 可以看到两个配置对象的 id 值不一样。由于配置文件只有一个,我们希望配置对象也只有一个。
需求二:配置对象全局唯一
交代一个背景,解释器在做 import 的时候是单一线程在跑的。有了这个前提我们可以少写一些加锁的代码,能少写一行算一行吧。
实例配置管理from dataclasses import dataclass@dataclassclass config(object): name:str= mysql port:int = 3306 _instance = none # 单例模式 def __new__(cls, *args, **kw): if cls._instance is none: cls._instance = object.__new__(cls, *args, **kw) return cls._instance 用 python 就是这么的简单,几行代码就搞定了。但是还是要测试一下顺带引导出下一个需求。
in [4]: a = config()in [5]: b = config()in [6]: id(a)out[6]: 4414751568in [7]: id(b)out[7]: 4414751568 现在配置对象已经是单例了,但还有一个问题,它的每个配置项的值都是默认值,我们当然是希望它在创建对象的时候是使用配置文件中的值啦。下面看需求三怎么实现。
需求三:根据配置文件构造配置对象
假设我们的配置文件被 “持久化” 到了 /tmp/config.json ,现在就可以写读取配置文件并更新配置对象值的代码了。
实例配置管理import jsonimport loggingfrom pathlib import pathfrom dataclasses import dataclass@dataclassclass config(object): name:str= mysql port:int = 3306 _instance = none # 单例模式 def __new__(cls, *args, **kw): if cls._instance is none: cls._instance = object.__new__(cls, *args, **kw) return cls._instance # 读取配置文件 def __post_init__(self): 如果配置文件存在就用配置文件中的值,覆盖默认值。在这个过程中如果遇到异常就保持默认值 if (config_file:=path(/tmp/config.json)) and config_file.exists(): try: with open(config_file) as f: json_data = json.loads(f.read()) self.__dict__.update(json_data) except exception as err: pass else: logging.warn(config file '{}' not exists. well using defautl values ..format(config_file)) 假设我们的配置文件内容是这样的。
cat /tmp/config.json { name: trump, port: 8848} 下面的测试一下
in [2]: a = config()in [3]: aout[3]: config(name='trump', port=8848)in [4]: b = config()in [5]: bout[5]: config(name='trump', port=8848)in [6]: a == bout[6]: true 可以看到 name 和 port 已经没有使用默认的 mysql 和 3306 了,而是使用了配置文件中的值。
到这里我们只剩下最后一个需求,就是在程序退出的时候,把配置对象的值更新回配置文件。这个就看需求四怎么写。
需求四:程序退出前自动持久化配置对象到配置文件
解释器在退出前有个钩子(atexit),我们可以在这里指定回调函数,这个时候保存配置文件再适合不过。
实例配置管理import jsonimport atexitimport loggingfrom pathlib import pathfrom dataclasses import dataclass, asdict@dataclassclass config(object): name:str= mysql port:int = 3306 _instance = none # 单例模式 def __new__(cls, *args, **kw): if cls._instance is none: cls._instance = object.__new__(cls, *args, **kw) return cls._instance # 读取配置文件 def __post_init__(self): 如果配置文件存在就用配置文件中的值,覆盖默认值;在这个过程中如果遇到异常就保持默认值。程序退出时持久到到配置到文件。 if (config_file:=path(/tmp/config.json)) and config_file.exists(): try: with open(config_file) as f: json_data = json.loads(f.read()) self.__dict__.update(json_data) except exception as err: pass else: logging.warn(config file '{}' not exists. well using defautl values ..format(config_file)) # 程序退出时保存配置到配置文件 /tmp/config.json def sync_to_disk(): json_str = json.dumps(asdict(self), indent=4) with open(config_file, 'w') as f: logging.warning(save configs to '{}' .format(config_file)) f.write(json_str) atexit.register(sync_to_disk) 验证一下
in [1]: from appconfig import configin [2]: a = config()in [3]: a.nameout[3]: 'trump'in [4]: a.name = hello-worldin [5]: exit()warningsave configs to '/tmp/config.json' 看日志是已经把配置项更新回配置文件了,但是还是 cat 确认一下为好。
cat /tmp/config.json { name: hello-world, port: 8848} 可以看到确实已经把配置项的值更新到文件了。
美陆军2024年完成IVAS头显测试,单兵AR头显项目加速发展
AIoT企业“上云”势头正盛,谁为“选云用云”保驾护航?
SK海力士量产世界最高238层4D NAND闪存
高通携手诺基亚Here提升室内定位技术
身藏小小传感器,穿越冬奥看滑雪
Python程序配置文件管理的最佳工程实践
3D打印热交换器应用中存在的基本注意事项
充电桩和可穿戴该怎么选
FRAM存储器技术和标准的CMOS制造工艺相互兼容
就智能家居来说 CES 2017谁是赢家?
LTC2983 具自动冷结点补偿的热电偶测量
高低温试验箱的温度稳定性-贝尔试验设备
能源管理系统针对医院有哪些特色功能
锂离子电池充电芯片CN3051-2A应用电路
传法拉利版iPhone8将有大变化!5.2寸超高屏占比或搭配双电池
苹果M1处理器完成乔布斯完全制造全部Mac零组件的愿景
低调养眼魅力十足 亮黑色华为P10 Plus今日预售来袭
香港大学首创光热电统一理论可解决LED灯泡发光不均与寿命问题
2009年越南摩托车展 │越南汽车展 │越南汽摩配件展
全球首条大功率紫外LED芯片量产生产线在长治正式投产 未来将成为一个新的万亿产业