Key features implemented: - Updated package metadata and dependencies in PKG-INFO, setup files - Added main.py entry point for backward compatibility with README launch method - Enhanced CLI with config options, system info command, and proper signal handling - Implemented minimal PluginManager loading only plugin-loader core plugin - Refactored PluginLoader to follow minimal core design, removed sandbox/isolation complexity - Updated auto-dependency plugin with safer PL injection mechanism and disabled pl_injection - Removed legacy plugin files (firewall, frp_proxy, ftp_server, multi_lang_deploy, ops_toolbox, security_gateway) as functionality moved to core plugin system - Improved gitignore with comprehensive ignore patterns The changes implement a minimal core framework design where only the plugin-loader is directly loaded by the core, with all other plugins managed through the PL injection mechanism, significantly simplifying the architecture.
210 lines
6.7 KiB
Python
210 lines
6.7 KiB
Python
"""插件加载增强器"""
|
|
from ..circuit.breaker import CircuitBreaker
|
|
from ..recovery.health import HealthChecker
|
|
from ..recovery.auto_fix import AutoRecovery
|
|
from ..utils.logger import ProLogger
|
|
from .config import ProConfig
|
|
|
|
|
|
class PluginLoaderEnhancer:
|
|
"""插件加载增强器 - 为现有 plugin-loader 提供高级机制"""
|
|
|
|
def __init__(self, plugin_manager, config: ProConfig):
|
|
self.pm = plugin_manager
|
|
self.config = config
|
|
self._breakers = {}
|
|
self._health_checker = None
|
|
self._auto_recovery = AutoRecovery(
|
|
config.auto_recovery.max_attempts,
|
|
config.auto_recovery.delay
|
|
)
|
|
self._enhanced = False
|
|
|
|
def enhance(self):
|
|
"""增强 plugin-loader"""
|
|
if self._enhanced:
|
|
return
|
|
|
|
ProLogger.info("enhancer", "开始增强 plugin-loader...")
|
|
|
|
# 1. 为所有插件创建熔断器
|
|
self._setup_circuit_breakers()
|
|
|
|
# 2. 包装启动方法(带重试和容错)
|
|
self._wrap_start_methods()
|
|
|
|
# 3. 启动健康检查
|
|
self._start_health_check()
|
|
|
|
self._enhanced = True
|
|
ProLogger.info("enhancer", "增强完成,共增强 {} 个插件".format(
|
|
len(self.pm.plugins)
|
|
))
|
|
|
|
def _setup_circuit_breakers(self):
|
|
"""为所有插件创建熔断器"""
|
|
for name, info in self.pm.plugins.items():
|
|
self._breakers[name] = CircuitBreaker(
|
|
self.config.circuit_breaker.failure_threshold,
|
|
self.config.circuit_breaker.recovery_timeout,
|
|
self.config.circuit_breaker.half_open_requests
|
|
)
|
|
ProLogger.debug("enhancer", f"为 {name} 创建熔断器")
|
|
|
|
def _wrap_start_methods(self):
|
|
"""包装启动方法"""
|
|
original_start_all = getattr(self.pm, 'start_all', None)
|
|
if original_start_all:
|
|
def wrapped_start_all():
|
|
self._safe_start_all()
|
|
|
|
self.pm.start_all = wrapped_start_all
|
|
ProLogger.info("enhancer", "已包装 start_all 方法")
|
|
|
|
original_init_and_start = getattr(
|
|
self.pm, 'init_and_start_all', None
|
|
)
|
|
if original_init_and_start:
|
|
def wrapped_init_and_start():
|
|
self._safe_init_and_start_all()
|
|
|
|
self.pm.init_and_start_all = wrapped_init_and_start
|
|
ProLogger.info("enhancer", "已包装 init_and_start_all 方法")
|
|
|
|
def _safe_init_and_start_all(self):
|
|
"""安全的初始化并启动"""
|
|
ordered = self._get_ordered_plugins()
|
|
|
|
# 安全初始化
|
|
for name in ordered:
|
|
self._safe_call(name, 'init', '初始化')
|
|
|
|
# 安全启动
|
|
for name in ordered:
|
|
self._safe_call(name, 'start', '启动')
|
|
|
|
def _safe_start_all(self):
|
|
"""安全启动所有"""
|
|
for name in self.pm.plugins:
|
|
self._safe_call(name, 'start', '启动')
|
|
|
|
def _safe_call(self, name: str, method: str, action: str):
|
|
"""安全调用插件方法(带熔断和重试)"""
|
|
info = self.pm.plugins.get(name)
|
|
if not info:
|
|
return
|
|
|
|
instance = info.get("instance")
|
|
if not instance or not hasattr(instance, method):
|
|
return
|
|
|
|
breaker = self._breakers.get(name)
|
|
if not breaker:
|
|
# 没有熔断器,直接调用
|
|
try:
|
|
getattr(instance, method)()
|
|
except Exception as e:
|
|
ProLogger.error("safe", f"{name} {action}失败: {e}")
|
|
self._on_plugin_error(name, info, str(e))
|
|
return
|
|
|
|
# 有熔断器,包装调用
|
|
def do_call():
|
|
return getattr(instance, method)()
|
|
|
|
try:
|
|
breaker.call(do_call)
|
|
info["info"].error_count = 0
|
|
ProLogger.info("safe", f"{name} {action}成功")
|
|
except Exception as e:
|
|
ProLogger.error("safe", f"{name} {action}失败: {e}")
|
|
self._on_plugin_error(name, info, str(e))
|
|
|
|
def _on_plugin_error(self, name: str, info: dict, error: str):
|
|
"""插件错误处理"""
|
|
info["info"].error_count += 1
|
|
info["info"].last_error = error
|
|
|
|
# 自动恢复
|
|
if self.config.auto_recovery.enabled:
|
|
plugin_dir = info.get("dir")
|
|
module = info.get("module")
|
|
|
|
if plugin_dir:
|
|
result = self._auto_recovery.attempt_recovery(
|
|
name, plugin_dir, module, info.get("instance")
|
|
)
|
|
if result:
|
|
info["instance"] = result
|
|
info["info"].error_count = 0
|
|
ProLogger.info("recovery", f"{name} 自动恢复成功")
|
|
|
|
def _start_health_check(self):
|
|
"""启动健康检查"""
|
|
self._health_checker = HealthChecker(
|
|
self.config.health_check.interval,
|
|
self.config.health_check.timeout,
|
|
self.config.health_check.max_failures
|
|
)
|
|
|
|
for name, info in self.pm.plugins.items():
|
|
self._health_checker.add_plugin(name, info["instance"])
|
|
|
|
self._health_checker.start(
|
|
on_failure_callback=self._on_health_check_failure
|
|
)
|
|
ProLogger.info("enhancer", "健康检查已启动")
|
|
|
|
def _on_health_check_failure(self, name: str):
|
|
"""健康检查失败回调"""
|
|
ProLogger.error("health", f"插件 {name} 健康检查失败")
|
|
|
|
info = self.pm.plugins.get(name)
|
|
if not info:
|
|
return
|
|
|
|
plugin_dir = info.get("dir")
|
|
module = info.get("module")
|
|
|
|
if plugin_dir:
|
|
result = self._auto_recovery.attempt_recovery(
|
|
name, plugin_dir, module, info.get("instance")
|
|
)
|
|
if result:
|
|
info["instance"] = result
|
|
self._health_checker.reset_failure_count(name)
|
|
ProLogger.info("recovery", f"{name} 健康恢复成功")
|
|
|
|
def _get_ordered_plugins(self) -> list[str]:
|
|
"""获取按依赖排序的插件列表"""
|
|
ordered = []
|
|
visited = set()
|
|
|
|
def visit(name):
|
|
if name in visited:
|
|
return
|
|
visited.add(name)
|
|
|
|
info = self.pm.plugins.get(name)
|
|
if not info:
|
|
return
|
|
|
|
for dep in info["info"].dependencies:
|
|
clean_dep = dep.rstrip("}")
|
|
if clean_dep in self.pm.plugins:
|
|
visit(clean_dep)
|
|
|
|
ordered.append(name)
|
|
|
|
for name in self.pm.plugins:
|
|
visit(name)
|
|
|
|
return ordered
|
|
|
|
def disable(self):
|
|
"""禁用增强器"""
|
|
if self._health_checker:
|
|
self._health_checker.stop()
|
|
self._enhanced = False
|
|
ProLogger.info("enhancer", "增强器已禁用")
|