loader_config.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import os, yaml, datetime
  2. from typing import Any, List, Callable
  3. from watchdog.observers import Observer
  4. from watchdog.events import FileSystemEventHandler
  5. from .config import Config
  6. class ConfigReloadHandler(FileSystemEventHandler):
  7. """配置文件变更处理器"""
  8. def __init__(self, callback: Callable):
  9. self.callback = callback
  10. def on_modified(self, event):
  11. if not event.is_directory:
  12. self.callback()
  13. class ConfigLoader:
  14. """配置加载器"""
  15. _instance = None
  16. _observers = []
  17. # 环境变量映射配置
  18. ENV_MAPPING = {
  19. 'APP_NAME': 'app.name',
  20. 'APP_VERSION': 'app.version',
  21. 'APP_DEBUG': 'app.debug',
  22. 'DB_HOST': 'database.host',
  23. 'DB_PORT': 'database.port',
  24. 'DB_USER': 'database.user',
  25. 'DB_PASSWORD': 'database.password',
  26. 'DB_NAME': 'database.name',
  27. }
  28. def __new__(cls):
  29. if cls._instance is None:
  30. cls._instance = super().__new__(cls)
  31. cls._instance._load_config()
  32. return cls._instance
  33. def _load_env_vars(self, config: Config) -> Config:
  34. """加载环境变量配置"""
  35. for env_key, config_path in self.ENV_MAPPING.items():
  36. value = os.getenv(env_key)
  37. if value is None:
  38. continue
  39. try:
  40. # 按路径分割
  41. keys = config_path.split('.')
  42. current = config
  43. # 遍历配置路径
  44. for key in keys[:-1]:
  45. if not hasattr(current, key):
  46. setattr(current, key, Config())
  47. current = getattr(current, key)
  48. # 获取目标字段类型
  49. last_key = keys[-1]
  50. target_type = type(getattr(current, last_key, str))
  51. # 根据目标类型转换值
  52. if target_type == bool:
  53. # 支持多种布尔值表示
  54. value_lower = value.lower()
  55. if value_lower in ('true', 'yes', 'y', '1'):
  56. setattr(current, last_key, True)
  57. elif value_lower in ('false', 'no', 'n', '0'):
  58. setattr(current, last_key, False)
  59. else:
  60. raise ValueError(f"环境变量 {env_key} 的值 {value} 不是有效的布尔值")
  61. elif target_type == int:
  62. # 支持数字字符串
  63. try:
  64. setattr(current, last_key, int(float(value)))
  65. except (ValueError, TypeError):
  66. raise ValueError(f"环境变量 {env_key} 的值 {value} 不是有效的整数")
  67. elif target_type == float:
  68. # 支持浮点数字符串
  69. try:
  70. setattr(current, last_key, float(value))
  71. except (ValueError, TypeError):
  72. raise ValueError(f"环境变量 {env_key} 的值 {value} 不是有效的浮点数")
  73. elif target_type == list:
  74. # 支持数组类型,格式:value1,value2,value3
  75. try:
  76. setattr(current, last_key,
  77. [item.strip() for item in value.split(',')])
  78. except Exception:
  79. raise ValueError(
  80. f"环境变量 {env_key} 的值 {value} 不是有效的数组格式")
  81. elif target_type == set:
  82. # 支持集合类型,格式:value1,value2,value3
  83. try:
  84. setattr(current, last_key,
  85. set(item.strip() for item in value.split(',')))
  86. except Exception:
  87. raise ValueError(
  88. f"环境变量 {env_key} 的值 {value} 不是有效的集合格式")
  89. elif target_type == dict:
  90. # 支持字典类型,格式:key1:value1,key2:value2
  91. try:
  92. setattr(
  93. current, last_key,
  94. dict(item.split(':') for item in value.split(',')))
  95. except Exception:
  96. raise ValueError(
  97. f"环境变量 {env_key} 的值 {value} 不是有效的字典格式")
  98. elif target_type == datetime.datetime:
  99. # 支持时间类型,格式:YYYY-MM-DD HH:MM:SS
  100. try:
  101. setattr(
  102. current, last_key,
  103. datetime.datetime.strptime(value,
  104. '%Y-%m-%d %H:%M:%S'))
  105. except Exception:
  106. raise ValueError(
  107. f"环境变量 {env_key} 的值 {value} 不是有效的时间格式")
  108. else:
  109. setattr(current, last_key, value)
  110. except Exception as e:
  111. print(f"[ERROR] 加载环境变量 {env_key} 时出错: {str(e)}")
  112. continue
  113. return config
  114. def _load_yaml_config(self, file_path: str) -> dict:
  115. """加载YAML配置文件"""
  116. try:
  117. if os.path.exists(file_path):
  118. with open(file_path, 'r', encoding='utf-8') as f:
  119. config = yaml.safe_load(f) or {}
  120. if not isinstance(config, dict):
  121. raise ValueError(f"配置文件 {file_path} 格式错误:应为字典格式")
  122. return config
  123. else:
  124. print(f"配置文件 {file_path} 不存在")
  125. return {}
  126. except yaml.YAMLError as e:
  127. print(f"YAML解析错误:{str(e)}")
  128. return {}
  129. except Exception as e:
  130. print(f"加载配置文件 {file_path} 时发生错误:{str(e)}")
  131. return {}
  132. def _load_config(self):
  133. """加载配置"""
  134. # 初始化配置实例
  135. self._config = Config()
  136. self._callbacks: List[Callable] = []
  137. # 1. 加载默认值(通过Config类初始化)
  138. # 2. 加载通用配置
  139. self._config.update_from_dict(
  140. self._load_yaml_config('SERVER/app/application.yml'))
  141. # 3. 加载指定环境配置
  142. env = os.getenv('ENV', 'dev')
  143. self._config.update_from_dict(
  144. self._load_yaml_config(f'SERVER/app/application-{env}.yml'))
  145. # 4. 最后加载环境变量(最高优先级)
  146. self._config = self._load_env_vars(self._config)
  147. # 启动配置监控
  148. self._start_config_watcher()
  149. def _start_config_watcher(self):
  150. """启动配置文件监控"""
  151. event_handler = ConfigReloadHandler(self.reload_config)
  152. observer = Observer()
  153. observer.schedule(event_handler, path='SERVER/app/', recursive=False)
  154. observer.start()
  155. self._observers.append(observer)
  156. def reload_config(self):
  157. """重新加载配置"""
  158. self._load_config()
  159. for callback in self._callbacks:
  160. callback()
  161. def get(self, key: str, default=None) -> Any:
  162. """获取配置值"""
  163. keys = key.split('.')
  164. current = self._config
  165. try:
  166. for k in keys:
  167. current = getattr(current, k)
  168. return current
  169. except (KeyError, AttributeError):
  170. return default
  171. def on_config_change(self, callback: Callable):
  172. """注册配置变更回调"""
  173. self._callbacks.append(callback)
  174. def __del__(self):
  175. """清理资源"""
  176. for observer in self._observers:
  177. observer.stop()
  178. observer.join()