一切皆为插件的开发者工具运行时框架
🧩 核心特性:
- 插件热插拔 (importlib 动态加载)
- 依赖自动解析 (拓扑排序 + 循环检测)
- 企业级稳定 (熔断/降级/重试/隔离)
- 事件驱动 (发布/订阅事件总线)
- 完整配置 (YAML 配置 + 热重载)
139 lines
4.1 KiB
Python
139 lines
4.1 KiB
Python
"""依赖解析插件 - 拓扑排序 + 循环依赖检测"""
|
||
from typing import Any, Optional
|
||
|
||
from oss.plugin.types import Plugin, register_plugin_type
|
||
|
||
|
||
class DependencyError(Exception):
|
||
"""依赖错误"""
|
||
pass
|
||
|
||
|
||
class DependencyResolver:
|
||
"""依赖解析器"""
|
||
|
||
def __init__(self):
|
||
self.graph: dict[str, list[str]] = {} # 插件名 -> 依赖列表
|
||
|
||
def add_plugin(self, name: str, dependencies: list[str]):
|
||
"""添加插件及其依赖"""
|
||
self.graph[name] = dependencies
|
||
|
||
def resolve(self) -> list[str]:
|
||
"""解析依赖,返回拓扑排序后的插件列表
|
||
|
||
例如:A 依赖 B,B 依赖 C
|
||
图: A -> [B], B -> [C], C -> []
|
||
结果: [C, B, A] (先启动没有依赖的,再启动依赖它们的)
|
||
"""
|
||
# 检测循环依赖
|
||
self._detect_cycles()
|
||
|
||
# 拓扑排序 (Kahn 算法 - 反向)
|
||
# in_degree[name] = name 依赖的插件数量
|
||
in_degree: dict[str, int] = {name: 0 for name in self.graph}
|
||
# 反向图: who_depends_on[dep] = [name1, name2, ...] (谁依赖 dep)
|
||
who_depends_on: dict[str, list[str]] = {name: [] for name in self.graph}
|
||
|
||
for name, deps in self.graph.items():
|
||
for dep in deps:
|
||
if dep in in_degree:
|
||
in_degree[name] += 1 # name 依赖 dep,所以 name 的入度 +1
|
||
who_depends_on[dep].append(name) # dep 被 name 依赖
|
||
|
||
# 从没有依赖的插件开始
|
||
queue = [name for name, degree in in_degree.items() if degree == 0]
|
||
result = []
|
||
|
||
while queue:
|
||
node = queue.pop(0)
|
||
result.append(node)
|
||
# node 已启动,减少依赖它的插件的入度
|
||
for dependent in who_depends_on.get(node, []):
|
||
in_degree[dependent] -= 1
|
||
if in_degree[dependent] == 0:
|
||
queue.append(dependent)
|
||
|
||
if len(result) != len(self.graph):
|
||
raise DependencyError("无法解析依赖,可能存在循环依赖")
|
||
|
||
return result
|
||
|
||
def _detect_cycles(self):
|
||
"""检测循环依赖"""
|
||
visited = set()
|
||
rec_stack = set()
|
||
|
||
def dfs(node: str) -> bool:
|
||
visited.add(node)
|
||
rec_stack.add(node)
|
||
|
||
for dep in self.graph.get(node, []):
|
||
if dep not in visited:
|
||
if dfs(dep):
|
||
return True
|
||
elif dep in rec_stack:
|
||
raise DependencyError(f"检测到循环依赖: {node} -> {dep}")
|
||
|
||
rec_stack.remove(node)
|
||
return False
|
||
|
||
for node in self.graph:
|
||
if node not in visited:
|
||
if dfs(node):
|
||
raise DependencyError(f"检测到循环依赖涉及: {node}")
|
||
|
||
def get_missing(self) -> list[str]:
|
||
"""获取缺失的依赖"""
|
||
all_deps = set()
|
||
for deps in self.graph.values():
|
||
all_deps.update(deps)
|
||
all_plugins = set(self.graph.keys())
|
||
return list(all_deps - all_plugins)
|
||
|
||
|
||
class DependencyPlugin(Plugin):
|
||
"""依赖解析插件"""
|
||
|
||
def __init__(self):
|
||
self.resolver = DependencyResolver()
|
||
self.plugin_deps: dict[str, list[str]] = {}
|
||
|
||
def init(self, deps: dict = None):
|
||
"""初始化"""
|
||
pass
|
||
|
||
def start(self):
|
||
"""启动"""
|
||
pass
|
||
|
||
def stop(self):
|
||
"""停止"""
|
||
pass
|
||
|
||
def add_plugin(self, name: str, dependencies: list[str]):
|
||
"""添加插件及其依赖"""
|
||
self.plugin_deps[name] = dependencies
|
||
self.resolver.add_plugin(name, dependencies)
|
||
|
||
def resolve(self) -> list[str]:
|
||
"""解析依赖顺序"""
|
||
return self.resolver.resolve()
|
||
|
||
def get_missing_deps(self) -> list[str]:
|
||
"""获取缺失的依赖"""
|
||
return self.resolver.get_missing()
|
||
|
||
def get_order(self) -> list[str]:
|
||
"""获取插件加载顺序"""
|
||
return self.resolve()
|
||
|
||
|
||
# 注册类型
|
||
register_plugin_type("DependencyResolver", DependencyResolver)
|
||
register_plugin_type("DependencyError", DependencyError)
|
||
|
||
|
||
def New():
|
||
return DependencyPlugin()
|