From b4c046a59497c4e696b81d5416bb0c99d461fa56 Mon Sep 17 00:00:00 2001 From: liuwei Date: Wed, 6 May 2026 11:07:47 +0800 Subject: [PATCH] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=85=B3=E9=97=AD=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=83=AD=E5=8A=A0=E8=BD=BD=E6=89=AB=E7=9B=98=E7=9B=91?= =?UTF-8?q?=E5=90=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.example.yaml | 8 ++++++++ config.yaml | 8 ++++++++ configuration.py | 24 ++++++++++++++++++++++++ robot.py | 13 +++++++++++-- 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/config.example.yaml b/config.example.yaml index 2cda947..5bb4424 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -1,6 +1,14 @@ environment: "${ABOT_ENVIRONMENT:development}" plugin_dir: "${ABOT_PLUGIN_DIR:plugins}" +plugin_hot_reload: + # 线上默认关闭插件热加载扫描: + # 1. 避免后台线程周期性扫盘; + # 2. 降低 discover_plugins 带来的额外 IO; + # 3. 如确实需要热更新,再单独通过环境变量打开。 + enabled: "${ABOT_PLUGIN_HOT_RELOAD_ENABLED:false}" + interval_seconds: "${ABOT_PLUGIN_HOT_RELOAD_INTERVAL:600}" + db_config: pool_name: "${ABOT_DB_POOL_NAME:wechat_boot_pool}" pool_size: "${ABOT_DB_POOL_SIZE:10}" diff --git a/config.yaml b/config.yaml index 2cda947..5bb4424 100644 --- a/config.yaml +++ b/config.yaml @@ -1,6 +1,14 @@ environment: "${ABOT_ENVIRONMENT:development}" plugin_dir: "${ABOT_PLUGIN_DIR:plugins}" +plugin_hot_reload: + # 线上默认关闭插件热加载扫描: + # 1. 避免后台线程周期性扫盘; + # 2. 降低 discover_plugins 带来的额外 IO; + # 3. 如确实需要热更新,再单独通过环境变量打开。 + enabled: "${ABOT_PLUGIN_HOT_RELOAD_ENABLED:false}" + interval_seconds: "${ABOT_PLUGIN_HOT_RELOAD_INTERVAL:600}" + db_config: pool_name: "${ABOT_DB_POOL_NAME:wechat_boot_pool}" pool_size: "${ABOT_DB_POOL_SIZE:10}" diff --git a/configuration.py b/configuration.py index e3beb85..7ab5031 100644 --- a/configuration.py +++ b/configuration.py @@ -153,6 +153,20 @@ class Config(object): except (TypeError, ValueError): return default + @staticmethod + def _safe_bool(value, default: bool): + """把常见的字符串/数字配置安全转成布尔值。""" + if value is None: + return default + if isinstance(value, bool): + return value + text = str(value).strip().lower() + if text in {"1", "true", "yes", "y", "on"}: + return True + if text in {"0", "false", "no", "n", "off"}: + return False + return default + def _normalize_config(self, yconfig: dict) -> dict: """对解析后的配置做一次结构与类型归一化。""" normalized = copy.deepcopy(yconfig or {}) @@ -180,6 +194,15 @@ class Config(object): email_config["smtp_port"] = self._safe_int(email_config.get("smtp_port", 465), 465) normalized["email_config"] = email_config + plugin_hot_reload = dict(normalized.get("plugin_hot_reload", {}) or {}) + # 插件热加载本质上是一个持续扫盘线程: + # 1. 本地开发时它很方便,但线上稳定运行时意义不大; + # 2. 用户当前明确希望先停掉这类自动扫盘行为,降低不必要的 IO 干扰; + # 3. 因此这里把 enabled / interval_seconds 做成标准化配置,便于后续按环境开关。 + plugin_hot_reload["enabled"] = self._safe_bool(plugin_hot_reload.get("enabled", False), False) + plugin_hot_reload["interval_seconds"] = self._safe_int(plugin_hot_reload.get("interval_seconds", 600), 600) + normalized["plugin_hot_reload"] = plugin_hot_reload + return normalized @classmethod @@ -423,6 +446,7 @@ class Config(object): # 后续如果逐步引入更严格的配置对象,也可以先不动业务代码。 self.environment = str(self.resolved_config.get("environment", "development") or "development").strip() self.plugin_dir = str(self.resolved_config.get("plugin_dir", "plugins") or "plugins").strip() + self.plugin_hot_reload = self.resolved_config.get("plugin_hot_reload", {}) self.mariadb = self.resolved_config.get("db_config", {}) self.redis = self.resolved_config.get("redis_config", {}) self.email = self.resolved_config.get("email_config", {}) diff --git a/robot.py b/robot.py index 136a24a..83daf44 100644 --- a/robot.py +++ b/robot.py @@ -115,8 +115,17 @@ class Robot: self.plugin_manager = PluginManager(plugin_dir=getattr(self.config, "plugin_dir", "plugins")) self.plugin_manager.set_system_context(self.system_context) self.plugins = self.plugin_manager.load_all_plugins() - # 热加载改为低频扫描:每 60 秒检查一次插件文件变动 - self.plugin_manager.start_hot_reload_watcher(interval_seconds=60.0) + # 插件热加载默认关闭: + # 1. 它会持续轮询插件目录并调用 discover_plugins,线上运行会产生额外扫盘开销; + # 2. 对当前以“稳定运行”为主的场景,这类自动热更新收益远低于成本; + # 3. 若后续确实需要在线调试插件,可通过配置重新打开,并把轮询间隔调大。 + hot_reload_cfg = dict(getattr(self.config, "plugin_hot_reload", {}) or {}) + if bool(hot_reload_cfg.get("enabled")): + interval_seconds = max(float(hot_reload_cfg.get("interval_seconds", 600) or 600), 60.0) + self.plugin_manager.start_hot_reload_watcher(interval_seconds=interval_seconds) + self.LOG.info(f"插件热加载监听已启用,轮询间隔 {interval_seconds}s") + else: + self.LOG.info("插件热加载监听已禁用,启动阶段不再自动扫盘检查插件变更") self.system_job_loader = SystemJobLoader(self, self.system_job_db) self.system_job_loader.init_and_load() self.plugin_schedule_manager = PluginScheduleManager(self.plugin_manager, self.plugin_schedule_db)