本文目录导读:

在Python中实现配置热更新(无需重启应用即可更新配置),主要有以下几种常见方法:
使用配置文件 + 定时检查
最简单的实现方式
import time
import json
import os
from threading import Thread, Lock
class ConfigManager:
def __init__(self, config_path, check_interval=5):
self.config_path = config_path
self.config = {}
self.last_mtime = 0
self.check_interval = check_interval
self._lock = Lock()
self._load_config()
# 启动后台检查线程
self._watch_thread = Thread(target=self._watch_config, daemon=True)
self._watch_thread.start()
def _load_config(self):
"""加载配置文件"""
try:
with open(self.config_path, 'r', encoding='utf-8') as f:
new_config = json.load(f)
with self._lock:
self.config = new_config
self.last_mtime = os.path.getmtime(self.config_path)
print(f"配置已加载: {self.config}")
except Exception as e:
print(f"加载配置失败: {e}")
def _watch_config(self):
"""监控配置文件变化"""
while True:
try:
if os.path.exists(self.config_path):
current_mtime = os.path.getmtime(self.config_path)
if current_mtime > self.last_mtime:
print("检测到配置文件变更,重新加载...")
self._load_config()
except Exception as e:
print(f"监控配置出错: {e}")
time.sleep(self.check_interval)
def get(self, key, default=None):
with self._lock:
return self.config.get(key, default)
def get_all(self):
with self._lock:
return self.config.copy()
# 使用示例
config = ConfigManager('config.json', check_interval=2)
def main():
while True:
# 始终获取最新配置
db_host = config.get('db_host', 'localhost')
debug_mode = config.get('debug', False)
print(f"当前配置 - 数据库: {db_host}, 调试模式: {debug_mode}")
time.sleep(3)
# 运行主程序
# main()
使用 watchdog 库(更高效)
pip install watchdog
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import json
class ConfigWatcher(FileSystemEventHandler):
def __init__(self, callback):
self.callback = callback
self.last_modified = 0
def on_modified(self, event):
if event.is_directory:
return
if event.src_path.endswith('.json'):
current_time = time.time()
if current_time - self.last_modified > 1: # 防抖
self.last_modified = current_time
print(f"配置变更: {event.src_path}")
self.callback(event.src_path)
class HotConfig:
def __init__(self, config_path):
self.config_path = config_path
self.config = {}
self._load_config()
# 设置文件监控
self._setup_watcher()
def _load_config(self):
"""加载配置"""
try:
with open(self.config_path, 'r', encoding='utf-8') as f:
self.config = json.load(f)
print(f"配置已更新: {self.config}")
except Exception as e:
print(f"配置加载失败: {e}")
def _setup_watcher(self):
"""设置文件监控"""
import os
self.observer = Observer()
handler = ConfigWatcher(lambda path: self._load_config())
# 监控配置文件的目录
watch_dir = os.path.dirname(os.path.abspath(self.config_path))
self.observer.schedule(handler, watch_dir, recursive=False)
self.observer.start()
def get(self, key, default=None):
return self.config.get(key, default)
def update(self, key, value):
"""动态更新配置并保存到文件"""
self.config[key] = value
self._save_config()
def _save_config(self):
"""保存配置到文件"""
try:
with open(self.config_path, 'w', encoding='utf-8') as f:
json.dump(self.config, f, indent=2, ensure_ascii=False)
except Exception as e:
print(f"保存配置失败: {e}")
# 使用示例
config = HotConfig('config.json')
# 动态更新配置
config.update('max_connections', 100)
config.update('api_timeout', 30)
# 获取配置
print(config.get('max_connections'))
使用环境变量(适合容器化部署)
import os
import json
from typing import Any
class EnvConfig:
def __init__(self, prefix="APP_"):
self.prefix = prefix
self.config = {}
self._load_from_env()
def _load_from_env(self):
"""从环境变量加载配置"""
for key, value in os.environ.items():
if key.startswith(self.prefix):
config_key = key[len(self.prefix):].lower()
# 尝试转换类型
self.config[config_key] = self._parse_value(value)
def _parse_value(self, value: str) -> Any:
"""智能解析配置值"""
# 尝试转换为数字
try:
if '.' in value:
return float(value)
return int(value)
except ValueError:
pass
# 布尔值
if value.lower() in ('true', 'yes', '1'):
return True
if value.lower() in ('false', 'no', '0'):
return False
return value
def get(self, key, default=None):
return self.config.get(key, default)
def refresh(self):
"""手动刷新配置"""
self._load_from_env()
def __getattr__(self, name):
return self.config.get(name, None)
# 使用示例
config = EnvConfig()
# 设置环境变量(实际生产中在部署环境设置)
os.environ['APP_DB_HOST'] = 'localhost'
os.environ['APP_DEBUG'] = 'true'
os.environ['APP_PORT'] = '8080'
# 获取配置
print(config.get('db_host')) # localhost
print(config.get('debug')) # True
print(config.get('port')) # 8080
基于Web的管理接口
from flask import Flask, request, jsonify
import threading
import json
class WebConfig:
def __init__(self, config_path, web_port=5000):
self.config_path = config_path
self.config = {}
self.lock = threading.Lock()
self._load_config()
# 启动Web服务
self.app = Flask(__name__)
self._setup_routes()
# 在后台线程启动Flask
thread = threading.Thread(target=self._run_web, daemon=True)
thread.start()
def _load_config(self):
"""加载配置"""
try:
with open(self.config_path, 'r', encoding='utf-8') as f:
with self.lock:
self.config = json.load(f)
except Exception as e:
print(f"配置加载失败: {e}")
def _save_config(self):
"""保存配置"""
with self.lock:
with open(self.config_path, 'w', encoding='utf-8') as f:
json.dump(self.config, f, indent=2, ensure_ascii=False)
def _setup_routes(self):
@self.app.route('/config', methods=['GET'])
def get_config():
with self.lock:
return jsonify(self.config)
@self.app.route('/config', methods=['POST'])
def update_config():
data = request.json
if not data:
return jsonify({"error": "No data provided"}), 400
with self.lock:
self.config.update(data)
self._save_config()
return jsonify({"message": "配置更新成功", "config": self.config})
@self.app.route('/config/<key>', methods=['PUT'])
def update_single_config(key):
data = request.json
if not data or 'value' not in data:
return jsonify({"error": "需要提供value字段"}), 400
with self.lock:
self.config[key] = data['value']
self._save_config()
return jsonify({"message": f"配置 {key} 已更新"})
def _run_web(self):
self.app.run(host='0.0.0.0', port=5000, debug=False)
def get(self, key, default=None):
with self.lock:
return self.config.get(key, default)
# 使用示例
config = WebConfig('config.json', web_port=5000)
# 通过API更新配置
# curl -X POST http://localhost:5000/config -H "Content-Type: application/json" -d '{"debug": true, "max_connections": 200}'
# 或直接代码更新
def update_config_externally():
import requests
requests.post('http://localhost:5000/config',
json={'debug': True, 'max_connections': 200})
推荐的最佳实践
结合多种方式的热更新管理器
import json
import os
import time
import hashlib
from threading import Thread, Lock
from typing import Any, Dict, Optional
class AdvancedHotConfig:
"""高级热更新配置管理器"""
def __init__(self,
config_path: str,
check_interval: int = 5,
backup: bool = True):
self.config_path = config_path
self.check_interval = check_interval
self._config: Dict[str, Any] = {}
self._file_hash = ''
self._lock = Lock()
self._callbacks = []
self._backup = backup
self._init_config()
self._start_watching()
def _init_config(self):
"""初始化配置"""
if not os.path.exists(self.config_path):
# 创建默认配置
default_config = {
'debug': False,
'max_connections': 50,
'timeout': 30,
'log_level': 'INFO'
}
self._save_config(default_config)
else:
self._load_config()
def _load_config(self):
"""加载配置"""
try:
with open(self.config_path, 'r', encoding='utf-8') as f:
content = f.read()
new_hash = hashlib.md5(content.encode()).hexdigest()
if new_hash != self._file_hash:
new_config = json.loads(content)
with self._lock:
old_config = self._config.copy()
self._config = new_config
self._file_hash = new_hash
# 通知监听器
self._notify_listeners(old_config, new_config)
print(f"[{time.strftime('%H:%M:%S')}] 配置已更新")
except Exception as e:
print(f"加载配置失败: {e}")
def _save_config(self, config: Dict[str, Any] = None):
"""保存配置"""
if config is None:
config = self._config
try:
# 备份旧配置
if self._backup and os.path.exists(self.config_path):
backup_path = f"{self.config_path}.bak"
import shutil
shutil.copy2(self.config_path, backup_path)
with open(self.config_path, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
# 更新hash
content = json.dumps(config, indent=2, ensure_ascii=False)
self._file_hash = hashlib.md5(content.encode()).hexdigest()
except Exception as e:
print(f"保存配置失败: {e}")
def _start_watching(self):
"""启动配置监控"""
def watch_loop():
while True:
try:
if os.path.exists(self.config_path):
self._load_config()
except Exception as e:
print(f"监控配置出错: {e}")
time.sleep(self.check_interval)
thread = Thread(target=watch_loop, daemon=True)
thread.start()
def _notify_listeners(self, old_config: Dict, new_config: Dict):
"""通知配置变更监听器"""
changes = {}
for key in set(list(old_config.keys()) + list(new_config.keys())):
if old_config.get(key) != new_config.get(key):
changes[key] = {
'old': old_config.get(key),
'new': new_config.get(key)
}
if changes:
for callback in self._callbacks:
try:
callback(changes)
except Exception as e:
print(f"回调执行失败: {e}")
def on_change(self, callback):
"""注册配置变更回调"""
self._callbacks.append(callback)
return callback
def get(self, key: str, default: Any = None) -> Any:
"""获取配置值"""
with self._lock:
return self._config.get(key, default)
def update(self, key: str, value: Any):
"""更新单个配置"""
with self._lock:
self._config[key] = value
self._save_config()
def batch_update(self, updates: Dict[str, Any]):
"""批量更新配置"""
with self._lock:
self._config.update(updates)
self._save_config()
def get_all(self) -> Dict[str, Any]:
"""获取所有配置"""
with self._lock:
return self._config.copy()
def reload(self):
"""强制重新加载配置"""
self._load_config()
# 配置变更回调示例
def on_config_change(changes):
print(f"配置变更: {changes}")
for key, change in changes.items():
if key == 'debug':
print(f"调试模式: {change['new']}")
elif key == 'max_connections':
print(f"最大连接数已调整为: {change['new']}")
# 使用示例
config = AdvancedHotConfig('config.json', check_interval=3)
# 注册配置变更监听器
config.on_change(on_config_change)
# 主程序逻辑
def main():
while True:
debug_mode = config.get('debug', False)
max_conn = config.get('max_connections', 50)
if debug_mode:
print(f"[调试模式] 当前配置: {config.get_all()}")
else:
# 正常业务逻辑
pass
time.sleep(10)
# 运行主程序
if __name__ == '__main__':
# main()
pass
注意事项
- 线程安全:使用锁机制保护配置读写
- 异常处理:配置加载失败时使用默认值
- 性能考虑:避免频繁读取配置文件
- 配置文件格式:推荐使用JSON或YAML
- 配置验证:加载配置后验证必要字段
- 版本控制:考虑配置文件的版本管理
- 监控告警:配置变更通知和日志记录
选择哪种方式取决于你的具体需求:
- 简单应用:定时检查文件修改
- 生产环境:watchdog + 配置文件
- 容器部署:环境变量
- 需要远程管理:Web接口
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播