优化启动阶段插件与调度初始化耗时

This commit is contained in:
liuwei
2026-05-07 14:46:52 +08:00
parent 3e56c90ab7
commit 19a4ab6e98
3 changed files with 40 additions and 11 deletions

View File

@@ -117,6 +117,7 @@ class Robot:
# 初始化插件系统
self.LOG.debug("开始初始化插件系统...")
plugin_bootstrap_started_at = time.perf_counter()
self.plugin_registry = PluginRegistry()
self.plugin_modules = {} # 存储已加载的插件模块
self.plugins = {} # 存储已加载的插件实例
@@ -132,7 +133,10 @@ class Robot:
self.plugin_manager = PluginManager(plugin_dir=getattr(self.config, "plugin_dir", "plugins"))
self.plugin_manager.set_system_context(self.system_context)
plugin_load_started_at = time.perf_counter()
self.plugins = self.plugin_manager.load_all_plugins()
plugin_load_cost_ms = int((time.perf_counter() - plugin_load_started_at) * 1000)
self.LOG.info(f"插件加载完成: count={len(self.plugins)} cost={plugin_load_cost_ms}ms")
# 插件热加载默认关闭:
# 1. 它会持续轮询插件目录并调用 discover_plugins线上运行会产生额外扫盘开销
# 2. 对当前以“稳定运行”为主的场景,这类自动热更新收益远低于成本;
@@ -144,20 +148,45 @@ class Robot:
self.LOG.info(f"插件热加载监听已启用,轮询间隔 {interval_seconds}s")
else:
self.LOG.info("插件热加载监听已禁用,启动阶段不再自动扫盘检查插件变更")
system_job_started_at = time.perf_counter()
self.system_job_loader = SystemJobLoader(self, self.system_job_db)
self.system_job_loader.init_and_load()
# 启动阶段只做任务注册,不同步补跑历史漏执行任务:
# 1. 旧逻辑会在这里直接补跑系统任务,重任务会把主进程启动拖慢几十秒;
# 2. 用户看到的日志像“卡在插件系统初始化”,实际常常是补偿任务在阻塞;
# 3. 这里先保证主流程快速完成,后续如需人工补跑,可在后台单独触发。
self.system_job_loader.init_and_load(run_startup_compensation=False)
system_job_cost_ms = int((time.perf_counter() - system_job_started_at) * 1000)
self.LOG.info(f"系统任务装载完成: cost={system_job_cost_ms}ms")
plugin_schedule_started_at = time.perf_counter()
self.plugin_schedule_manager = PluginScheduleManager(self.plugin_manager, self.plugin_schedule_db)
self.plugin_schedule_manager.init_and_load()
# 插件调度同样关闭启动期同步补偿,避免某些定时任务在启动时直接执行。
self.plugin_schedule_manager.init_and_load(run_startup_compensation=False)
plugin_schedule_cost_ms = int((time.perf_counter() - plugin_schedule_started_at) * 1000)
self.LOG.info(f"插件调度装载完成: cost={plugin_schedule_cost_ms}ms")
# 将历史业务型系统任务迁移到插件调度配置,避免升级后出现“任务丢失”。
migration_started_at = time.perf_counter()
migration_result = self.plugin_schedule_manager.migrate_from_system_jobs(self.system_job_db)
if migration_result.get("migrated", 0) > 0:
self.LOG.info(f"系统任务迁移到插件任务完成: {migration_result}")
self.plugin_schedule_manager.reload_from_db()
migration_cost_ms = int((time.perf_counter() - migration_started_at) * 1000)
self.LOG.info(f"插件任务迁移检查完成: cost={migration_cost_ms}ms result={migration_result}")
# 迁移完成后,清理已下沉到插件层的系统任务,避免后台重复维护两套配置。
cleanup_started_at = time.perf_counter()
self._cleanup_migrated_system_jobs()
cleanup_cost_ms = int((time.perf_counter() - cleanup_started_at) * 1000)
# 加载插件
self.LOG.debug("插件系统初始化完成")
plugin_bootstrap_cost_ms = int((time.perf_counter() - plugin_bootstrap_started_at) * 1000)
self.LOG.info(
"插件系统初始化完成: "
f"total={plugin_bootstrap_cost_ms}ms, "
f"plugin_load={plugin_load_cost_ms}ms, "
f"system_jobs={system_job_cost_ms}ms, "
f"plugin_schedules={plugin_schedule_cost_ms}ms, "
f"migration={migration_cost_ms}ms, "
f"cleanup={cleanup_cost_ms}ms"
)
GroupBotManager.load_local_cache()
# 权限模块加载

View File

@@ -21,9 +21,9 @@ class PluginScheduleManager:
self._schedule_job_map: Dict[int, str] = {}
self._compensation_tolerance_seconds = 120
def init_and_load(self):
def init_and_load(self, *, run_startup_compensation: bool = False):
self.db.init_tables()
self.reload_from_db()
self.reload_from_db(run_startup_compensation=run_startup_compensation)
def migrate_from_system_jobs(self, system_job_db) -> Dict[str, int]:
"""把历史系统任务配置迁移到插件任务表(幂等)。"""
@@ -252,7 +252,7 @@ class PluginScheduleManager:
self.db.create_log(schedule_id, status, summary, detail)
return {"success": status == "success", "summary": summary, "detail": detail}
def reload_from_db(self):
def reload_from_db(self, *, run_startup_compensation: bool = True):
self.sync_defaults()
# 清理旧注册,避免重复
@@ -281,7 +281,7 @@ class PluginScheduleManager:
try:
trigger_type = row.get("trigger_type", "at_times")
trigger_config = row.get("trigger_config", {"time_list": ["09:00"]})
if self._should_compensate_once(schedule_id, trigger_type, trigger_config):
if run_startup_compensation and self._should_compensate_once(schedule_id, trigger_type, trigger_config):
logger.warning(
f"插件调度触发漏执行补偿: schedule_id={schedule_id}, "
f"plugin={row.get('plugin_name')}, action={row.get('action_key')}"

View File

@@ -191,10 +191,10 @@ class SystemJobLoader:
return False
return latest_log_at < (expected_at - timedelta(seconds=self._compensation_tolerance_seconds))
def init_and_load(self):
def init_and_load(self, *, run_startup_compensation: bool = False):
self.db.init_tables()
self._seed_defaults()
self.reload_from_db()
self.reload_from_db(run_startup_compensation=run_startup_compensation)
def _seed_defaults(self):
for item in self._job_defs.values():
@@ -212,7 +212,7 @@ class SystemJobLoader:
}
)
def reload_from_db(self):
def reload_from_db(self, *, run_startup_compensation: bool = True):
# 每次重载前先补齐默认任务,避免误删后无法恢复
self._seed_defaults()
@@ -275,7 +275,7 @@ class SystemJobLoader:
try:
trigger_type = row.get("trigger_type", definition["trigger_type"])
trigger_config = row.get("trigger_config", definition["trigger_config"])
if self._should_compensate_once(job_key, trigger_type, trigger_config):
if run_startup_compensation and self._should_compensate_once(job_key, trigger_type, trigger_config):
logger.warning(f"系统任务触发漏执行补偿: job_key={job_key}")
self._run_coro_blocking(_wrapped_handler())
except Exception as e: