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

news2024/11/25 23:03:20

前言

在开发和运维过程中,配置管理是一个非常重要但经常被忽视的环节。常用的配置文件格式包括env、ini和yaml等,它们非常适合模块级别的系统配置,尤其是一些敏感信息的配置,例如数据库连接字符串和密码等。但是,对于系统业务级别的配置,通常要求不需要重启服务即可更新,这就是我们今天要介绍的简单配置管理模块的意义所在。

系统配置表

首先,我们需要一个数据库表来存储配置项。这个表包括配置名称、配置值和配置描述等信息。以下是一个使用SQLAlchemy定义的配置表模型:

from sqlalchemy import (
    TEXT,
    TIMESTAMP,
    Column,
    Integer,
    String,
    func,
)

from app.models.base import Base, BaseMixin

class 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, Callable

import orjson
from app.models.sys_config import SysConfig

T = 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 = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super(ConfigManager, cls).__new__(cls)
        return cls._instance

    def __init__(self):
        self.configs: Dict[str, str] = {}
        self.initialized = False

    async 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 = True
        print("Configurations loaded into memory.")

    async def update_config(self, key: str, value: str, description: str = '', write_to_db=True):
        self.configs[key] = value
        if write_to_db:
            record = {'cfg_name': key, 'cfg_value': value}
            if description:
                record['cfg_desc'] = description
            await 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 method
        raise AttributeError(f"'ConfigManager' object has no attribute '{item}'")

使用示例

现在,我们已经有了一个完整的配置管理模块,让我们看一下如何在实际应用中使用它。以下是一个示例代码,展示了如何获取配置管理器并使用它来获取和更新配置项。

from app.services import config_services

async 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())

结语

通过上述代码示例,我们展示了如何创建一个简单而有效的配置管理模块,它能够动态加载和更新配置,支持多种数据类型的转换,并且在设计上注重高效和安全性。这个模块对于需要频繁更改业务逻辑配置而不希望重启服务的应用场景特别有用。

欢迎关注【程序员的开发手册】,我们将继续分享更多实用的开发技巧和工具,让您的开发之路更加顺畅。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1930205.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【系统架构设计】数据库系统(一)

数据库系统(一) 数据库模式与范式数据库的结构与模式数据模型关系代数数据的规范化反规范化 数据库设计事务管理备份与恢复分布式数据库系统数据仓库数据挖掘NoSQL大数据 数据库模式与范式 数据库的结构与模式 数据库技术中采用分级的方法将数据库的结…

释放DOE的能量,快速确定最佳工艺设置,节省时间、成本和资源

您是否希望降低成本、提高生产效率,并最大限度地减少行业对环境的影响? 所有行业,尤其是钢铁、铝、水泥和石化等能源密集型行业,都面临着应对这些挑战的持续压力。供应链压力、可持续发展、严格的监管环境、日益增长的消费者预期…

Transformer中高级位置编码的介绍和比较:Linear Rope、NTK、YaRN、CoPE

在处理诸如文本之类的序列时,排序信息显然是至关重要的。为了结合排序信息而不是将序列视为集合,对位置信息进行编码是至关重要的。位置编码通过为每个位置分配嵌入向量并将其添加到相应的标记表示来实现这一点。绝对和相对位置编码是最常见的两种位置编…

外贸行业汽车销售配件展示企业网站源码系统 带完整的源代码包以及搭建教程

系统概述 随着全球贸易的不断深化,外贸行业对于高效、专业的网站需求日益凸显。特别是对于汽车销售配件企业而言,一个功能全面、展示效果出色的网站源码系统,无疑是企业开拓海外市场、提升品牌形象的关键。本文将详细介绍一款专为外贸行业汽…

【Linux】文件管理常用命令【超详细】

文章目录 预防rm事故-血的教训😢1. 使用别名:2. 启用回收站:3. 只读文件系统: 一、文件管理1.1 touch-文件创建1.2 rm-文件删除1.3 mkdir-目录创建1.4 rmdir-目录删除1.5 pwd-显示当前目录1.6 cd-切换当前目录1.7 ls-列出文件和目…

鸿蒙语言基础类库:【@system.device (设备信息)】

设备信息 说明: 从API Version 6开始,该接口不再维护,推荐使用新接口[ohos.deviceInfo]进行设备信息查询。本模块首批接口从API version 3开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。 导入模块 import dev…

AI绘画Stable Diffusion 零基础入门 —AI 绘画原理与工具介绍,万字解析AI绘画的使用教程

大家好,我是设计师阿威 想要入门 AI 绘画,首先需要了解它的原理是什么样的。 其实很早就已经有人基于深度学习模型展开了对图像生成的研究了,但在那时,生成的图像分辨率和内容都非常抽象。 直到近两年,AI 产出的图像…

[openwrt-21.02]mt7981开启mwan3功能ping出现unreachable 问题分析及解决方案

mwan3 提供以下功能和能力 基于数值权重分配的出站 WAN 流量负载均衡或使用多个 WAN 接口进行故障转移 使用重复测试监控每个 WAN 连接,如果第一个 WAN 接口失去连接,则可以自动将出站流量路由到另一个 WAN 接口 创建出站流量规则以自定义哪些出站连接应使用哪个 WAN 接口(…

白门楼 下 | 第13集 | 曹操口头禅:故戏之耳 | 逐鹿群雄 | 三国演义

🙋大家好!我是毛毛张! 🌈个人首页: 神马都会亿点点的毛毛张 📌这篇博客分享的是《三国演义》文学剧本第Ⅰ部分《群雄逐鹿》的第13集《白门楼 下》的经典语句和文学剧本全集台词 文章目录 1.经典语句2.文学剧本台词 …

防火墙---带宽管理

防火墙的带宽管理:是指对防火墙设备的带宽进行管理和控制,以确保网络流量的合理分配和优化网络性能 带宽管理:是指限制网络流量的速率或控制网络流量的优先级,以确保网络的性能和可用性 核心: 带宽限制:…

环形数组复习

普通储存数据 接收数据 先要有个 缓存区 通常先建立一个数组 来保存数据 缓存区内存 如何分配和释放 此时 一包数据为 5字节 缓冲区为 17字节 方法一:每次清空缓冲区,重头开始存放数据 第一次 存放在 字节1-5 然后分析读取这次数据 后 先清除B…

2024华为数通HCIP-datacom最新题库(变题更新⑥)

请注意,华为HCIP-Datacom考试831已变题 请注意,华为HCIP-Datacom考试831已变题 请注意,华为HCIP-Datacom考试831已变题 近期打算考HCIP的朋友注意了,如果你准备去考试,还是用的之前的题库,切记暂缓。 1、…

《0基础》学习Python——第十三讲__面向对象

<类&#xff08;class&#xff09;> 一、面向对象概念 1、面向对象是一种编程思想和技术&#xff0c;它是一种将程序设计问题分解成对象的方式。每个对象都有自己的状态&#xff08;数据&#xff09;和行为&#xff08;方法&#xff09;&#xff0c;并且可以通过相互之间…

AMD software 将两个显示器合并为一个超宽显示器

最近玩游戏的时候&#xff0c;发现了一个骚操作。 可以将两个显示器&#xff08;更多个的自己去试&#xff0c;不知道&#xff09;组合为一个显示器&#xff0c;注意&#xff0c;这里说的不是将两个显示都连接电脑从而使用双屏显示器&#xff0c; 而是 将两个显示器组合为一个…

Logback格式简记

一、常见转换符 时间与日期 %d{pattern}&#xff1a;输出当前日期和时间。例如&#xff0c;%d{yyyy-MM-dd HH:mm:ss.SSS} 会输出 2024-07-11 15:34:55.123。 日志级别 %level 或 %p&#xff1a;输出日志级别&#xff0c;如 INFO, DEBUG, WARN, ERROR。 日志信息 %msg 或 …

【C++报错已解决】 “Undefined Reference“

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 前言 在编译程序时&#xff0c;遇到 “Undefined Reference” 报错总是令人头疼。这个错误提示通常意味着编译器找不到某个符号…

【Linux系统编程】shell命令以及运行原理 Linux权限

目录 一、shell命令以及运行原理 二、Linux权限的概念 2.1创建用户 2.2切换用户 2.3删除用户 三、Linux权限管理 3.1文件访问者的分类&#xff08;人&#xff09; 3.2文件类型和问权限&#xff08;事物属性&#xff09; 3.2.1文件类型 3.2.2基本权限代表的作用 3.…

泛微E-Cology WorkflowServiceXml SQL注入漏洞复现(QVD-2024-26136)

0x01 产品简介 泛微e-cology是一款由泛微网络科技开发的协同管理平台,支持人力资源、财务、行政等多功能管理和移动办公。 0x02 漏洞概述 2024年7月,泛微官方发布了新补丁,修复了一处SQL注入漏洞。经分析,攻击者无需认证即可利用该漏洞,建议受影响的客户尽快修复漏洞。…

mysql的主从复制(含位点复制和GTID复制)的代码实例

提示&#xff1a; master主库ip地址&#xff1a;192.168.137.2 从库s1的ip地址&#xff1a;192.168.137.11 从库s2的ip地址&#xff1a;192.168.137.22 主从复制的原理&#xff1a; MySQL主从复制是一个异步的复制过程&#xff0c;主要是通过二进制日志&#xff08;binary …

百度人脸识别Windows C++离线sdk C#接入

百度人脸识别Windows C离线sdk C#接入 目录 说明 设计背景 • 场景特点&#xff1a; • 客户特点&#xff1a; • 核心需求&#xff1a; SDK 包结构 效果 代码 说明 自己根据SDK封装了动态库&#xff0c;然后C#调用。 功能接口 设计背景 • 场景特点&#xff1a; -…