新手教学系列——简单的服务配置项集中管理

前言
在开发和运维过程中,配置管理是一个非常重要但经常被忽视的环节。常用的配置文件格式包括env、ini和yaml等,它们非常适合模块级别的系统配置,尤其是一些敏感信息的配置,例如数据库连接字符串和密码等。但是,对于系统业务级别的配置,通常要求不需要重启服务即可更新,这就是我们今天要介绍的简单配置管理模块的意义所在。
系统配置表
首先,我们需要一个数据库表来存储配置项。这个表包括配置名称、配置值和配置描述等信息。以下是一个使用SQLAlchemy定义的配置表模型:
from sqlalchemy import (TEXT,TIMESTAMP,Column,Integer,String,func,
)from app.models.base import Base, BaseMixinclass SysConfig(Base, BaseMixin):__tablename__ = 'sys_configs'__table_args__ = {"comment": "系统配置表"}id = Column(Integer, primary_key=True, autoincrement=True, comment='ID')cfg_name = Column(String(128), nullable=False, unique=True, comment='配置名称')cfg_value = Column(TEXT, nullable=True, comment='配置值')cfg_desc = Column(String(128), nullable=True, comment='配置描述')updated = Column(TIMESTAMP, index=True, server_default=func.now(), onupdate=func.now(), nullable=False, comment='更新时间')
配置管理类
接下来,我们需要一个配置管理类来加载和更新配置。这个类将会以单例模式运行,确保所有地方使用的配置都是一致的,并且在首次创建实例时自动加载所有配置项。我们使用异步操作来确保数据库操作的高效性。
import json
from typing import Any, Dict, Optional, Type, TypeVar, Callableimport orjson
from app.models.sys_config import SysConfigT = TypeVar('T')# 获取配置管理单例
async def get_config_manager():config_mgr = ConfigManager()if not config_mgr.initialized:await config_mgr.load_configs()return config_mgr# 配置管理类
class ConfigManager:_instance = Nonedef __new__(cls, *args, **kwargs):if cls._instance is None:cls._instance = super(ConfigManager, cls).__new__(cls)return cls._instancedef __init__(self):self.configs: Dict[str, str] = {}self.initialized = Falseasync def load_configs(self):cfg_rows = await SysConfig.get_all_async()for row in cfg_rows:self.configs[row['cfg_name']] = row['cfg_value']self.initialized = Trueprint("Configurations loaded into memory.")async def update_config(self, key: str, value: str, description: str = '', write_to_db=True):self.configs[key] = valueif write_to_db:record = {'cfg_name': key, 'cfg_value': value}if description:record['cfg_desc'] = descriptionawait SysConfig.upsert_async(records=[record], update_keys=['cfg_name'])print(f"Configuration updated: {key} = {value}")def _convert(self, key: str, type_: Type[T], default_value: Optional[T] = None) -> T:value = self.configs.get(key, default_value)if value is None:raise KeyError(f"Configuration key '{key}' not found and no default value provided.")try:if type_ == bool:return type_(value.lower() in ['true', '1', 'yes'])elif type_ == dict or type_ == list:return orjson.loads(value)return type_(value)except (ValueError, TypeError, json.JSONDecodeError) as e:raise ValueError(f"Error converting configuration value '{value}' to type {type_.__name__}: {e}")def __getattr__(self, item: str) -> Callable[[str, Optional[Any]], Any]:supported_types = {'int': int,'float': float,'bool': bool,'str': str,'dict': dict,'list': list,'json': dict,}if item in supported_types:def method(key: str, default_value: Optional[Any] = None) -> Any:return self._convert(key, supported_types[item], default_value)return methodraise AttributeError(f"'ConfigManager' object has no attribute '{item}'")
使用示例
现在,我们已经有了一个完整的配置管理模块,让我们看一下如何在实际应用中使用它。以下是一个示例代码,展示了如何获取配置管理器并使用它来获取和更新配置项。
from app.services import config_servicesasync def main():# 获取配置管理器单例config_mgr = await config_services.get_config_manager()# 更新配置await config_mgr.update_config('max_connections', '100', '最大连接数')await config_mgr.update_config('enable_feature', 'true', '启用新功能')await config_mgr.update_config('custom_dict', '{"key": "value"}', '自定义字典')await config_mgr.update_config('custom_list', '["item1", "item2"]', '自定义列表')# 获取并转换配置值try:max_connections = config_mgr.int('max_connections', 10)print(f"Max Connections: {max_connections}")enable_feature = config_mgr.bool('enable_feature', False)print(f"Enable Feature: {enable_feature}")custom_dict = config_mgr.dict('custom_dict', {})print(f"Custom Dict: {custom_dict}")custom_list = config_mgr.list('custom_list', [])print(f"Custom List: {custom_list}")except (KeyError, ValueError) as e:print(e)# 运行异步主函数
import asyncio
asyncio.run(main())
结语
通过上述代码示例,我们展示了如何创建一个简单而有效的配置管理模块,它能够动态加载和更新配置,支持多种数据类型的转换,并且在设计上注重高效和安全性。这个模块对于需要频繁更改业务逻辑配置而不希望重启服务的应用场景特别有用。
欢迎关注【程序员的开发手册】,我们将继续分享更多实用的开发技巧和工具,让您的开发之路更加顺畅。
相关文章:
新手教学系列——简单的服务配置项集中管理
前言 在开发和运维过程中,配置管理是一个非常重要但经常被忽视的环节。常用的配置文件格式包括env、ini和yaml等,它们非常适合模块级别的系统配置,尤其是一些敏感信息的配置,例如数据库连接字符串和密码等。但是,对于…...
《0基础》学习Python——第十三讲__面向对象
<类(class)> 一、面向对象概念 1、面向对象是一种编程思想和技术,它是一种将程序设计问题分解成对象的方式。每个对象都有自己的状态(数据)和行为(方法),并且可以通过相互之间…...
前端JS特效第42波:纯CSS实现的卡片切换效果
纯CSS实现的卡片切换效果,先来看看效果: 部分核心的代码如下: <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"utf-8"><title>纯CSS实现的卡片切换效果演示</title><l…...
2.10、matlab中字符、数字、矩阵、字符串和元胞合并为字符串并将字符串以不同格式写入读出excel
1、前言 在 MATLAB 中,可以使用不同的数据类型(字符、数字、矩阵、字符串和元胞)合并为字符串,然后将字符串以不同格式写入 Excel 文件。 以下是一个示例代码,展示如何将不同数据类型合并为字符串,并以不…...
中文科技核心论文发表
中文科技核心论文题目如下: 1.混凝土结构用纤维增强塑料筋的耐久性评述:适合建筑、结构、材料等专业 2.建筑工程用阻燃塑料的研究进展:适合建筑、材料专业 3.纤维增强热塑性塑料在面部护具中的应用研究:适合化工、医学、材料等专…...
使用 Flask 3 搭建问答平台(一):项目结构搭建
一、项目基本结构 二、app.py from flask import Flask import config from exts import db from models import UserModel from blueprints.qa import bp as qa_bp from blueprints.auth import bp as auth_bp# 创建一个Flask应用实例,__name__参数帮助Flask确定应…...
力扣经典题目之->用队列实现栈 的详细讲解和实现,看这一篇就够了!
一:题目 二:思路 1:先看两个概念: 更清晰的对比: 理解这两张图的不同对题目的一个函数( 返回栈顶元素) 会更好做 由图可知 : 返回栈顶函数即返回队列队尾即可 2:题…...
[JS]认识feach
介绍 feach是浏览器内置的api, 用于发送网络请求 请求方式对比 AJAX: 基于XMLHttpRequest接收请求, 使用繁琐Axios: 基于Promise的请求客户端, 在浏览器和node中均可使用, 使用简单fetch: 浏览器内置的api, 基于Promise, 功能简单 基础语法 <body><button>发请求…...
tomcat如何进行调优?
从两个方面考虑:内存和线程 首先启动Tomcat,实际上就是启动了一个JVM,所以可以按JVM调优的方式来进行调整,从而达到Tomcat优化的目的。 另外Tomcat中设计了一些缓存区,比如appReadBufSize、bufferPoolSize等缓存区来提…...
复现GMM文章(一):图1代码和数据
介绍 复现GMM文章的的Fig1图。 加载R包 library(tidyr)library(tidyverse)library(dplyr)library(ggsci)library(ggpubr)导入数据 所有的数据可以通过下列链接下载: 百度网盘链接: https://pan.baidu.com/s/1isKEK1G5I6X90KYqLufmWw 提取码: t9ca 图1B 数据 …...
链接追踪系列-07.logstash安装json_lines插件
进入docker中的logstash 容器内: jelexbogon ~ % docker exec -it 7ee8960c99a31e607f346b2802419b8b819cc860863bc283cb7483bc03ba1420 /bin/sh $ pwd /usr/share/logstash $ ls bin CONTRIBUTORS Gemfile jdk logstash-core modules tools x-pack …...
火山引擎数据飞轮实践:在电商场景中,如何建设全链路数据血缘?
数据作为新型生产要素,正支撑企业的数智化转型。但企业数字化建设也存在管理成本高、数据产品使用门槛高、数据资产价值不够的问题,其原因在于业务和数据之间没有形成双向良性驱动。 结合新时代企业数字化转型需求,火山引擎基于字节跳动十余…...
使用加密软件对企业来说有什么好处
泄密时间近年来层出不穷,一旦重要文件或数据被盗,无疑会对企业带来巨大的损失。 2024年3月,我国某高新科技企业遭境外黑客攻击,相关信息化系统及数据被加密锁定,生产经营活动被迫停止。企业生产经营活动受阻ÿ…...
STM32入门开发操作记录(二)——LED与蜂鸣器
目录 一、工程模板二、点亮主板1. 配置寄存器2. 调用库函数 三、LED1. 闪烁2. 流水灯 四、蜂鸣器 一、工程模板 参照第一篇,新建工程目录ProjectMould,将先前打包好的Start,Library和User文件^C^V过来,并在Keil5内完成器件支持包的…...
n3.平滑升级和回滚
平滑升级和回滚 1. 平滑升级流程2. 平滑升级和回滚案例 有时候我们需要对Nginx版本进行升级以满足对其功能的需求,例如添加新模块,需要新功能,而此时 Nginx又在跑着业务无法停掉,这时我们就可能选择平滑升级 1. 平滑升级流程 平…...
C#WPF DialogHost.Show 弹出对话框并返回数据
在WPF中,使用DialogHost.Show方法显示一个对话框并获取返回数据,你需要定义一个对话框,并在对话框关闭时返回数据。以下是一个简单的例子: 首先,在主窗口中添加DialogHost控件: <MaterialDesign:DialogHost x:Name="dialogHost" /> 然后,创建一个对话…...
Kafka Producer发送消息流程之分区器和数据收集器
文章目录 1. Partitioner分区器2. 自定义分区器3. RecordAccumulator数据收集器 1. Partitioner分区器 clients/src/main/java/org/apache/kafka/clients/producer/KafkaProducer.java,中doSend方法,记录了生产者将消息发送的流程,其中有一步…...
Codeforces Round 958 (Div. 2)
C o d e f o r c e s R o u n d 958 ( D i v . 2 ) \Huge{Codeforces Round 958 (Div. 2)} CodeforcesRound958(Div.2) 文章目录 Problems A. Split the Multiset题意思路标程 Problems B. Make Majority题意思路标程 Problems C. Increasing Sequence with Fixed OR题意思路标…...
<数据集>猫狗识别数据集<目标检测>
数据集格式:VOCYOLO格式 图片数量:3686张 标注数量(xml文件个数):3686 标注数量(txt文件个数):3686 标注类别数:2 标注类别名称:[cat, dog] 序号类别名称图片数框数1cat118811892dog24982498 使用标…...
Figma 中文版指南:获取和安装汉化插件
Figma是一种主流的在线团队合作设计工具,也是一种基于 Web 端的设计工具。在当今的设计时代,Figma 的使用满足了每个人的设计需求,不仅可以实现在线编辑,还可以方便日常管理,有效提高工作效率。然而,相信很…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
Unity VR/MR开发-VR开发与传统3D开发的差异
视频讲解链接:【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...
