Files
SenSu/services/process_isolated.py
AskaEth 4b26b6f086 v0.4+v0.5: TUI dashboard, tab completion, systemd, Docker, process isolation
- New: services/sysmon_widget.py (CPU/MEM/DISK/NET real-time)
- Enhanced: tui_service.py (4-section layout, plugin status panel)
- Enhanced: command_service.py + tui (Tab completion)
- New: deploy/sensu.service (systemd)
- New: deploy/Dockerfile (Alpine, 4200+4240)
- New: services/process_isolated.py (multiprocess plugin isolation)
- Tests: 23/23 passing
2026-06-10 19:08:16 +08:00

69 lines
2.5 KiB
Python

#!/usr/bin/env python3
"""插件进程隔离 — 在子进程中运行插件,崩溃不影响框架"""
import asyncio, subprocess, json, os, logging, tempfile
from multiprocessing import Process, Queue
from typing import Dict, Any
logger = logging.getLogger(__name__)
def _plugin_runner(plugin_path: str, config_json: str, cmd_queue: Queue, result_queue: Queue):
"""子进程入口 - 加载插件并监听命令队列"""
import sys, importlib.util, json as j
sys.path.insert(0, os.path.dirname(plugin_path))
spec = importlib.util.spec_from_file_location("plugin", plugin_path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
plugin = mod.Plugin(os.path.basename(os.path.dirname(plugin_path)),
j.loads(config_json), None)
asyncio.run(plugin.initialize())
while True:
cmd = cmd_queue.get()
if cmd == "__SHUTDOWN__":
asyncio.run(plugin.shutdown())
break
try:
method = getattr(plugin, cmd.get("method", ""), None)
if method:
result = method(*cmd.get("args", []))
result_queue.put({"ok": True, "result": str(result)})
else:
result_queue.put({"ok": False, "error": f"method not found: {cmd.get(method)}"})
except Exception as e:
result_queue.put({"ok": False, "error": str(e)})
class IsolatedPlugin:
"""进程隔离插件包装器"""
def __init__(self, plugin_name: str, plugin_path: str, config: dict):
self.name = plugin_name
self.cmd_queue = Queue()
self.result_queue = Queue()
self.process = Process(
target=_plugin_runner,
args=(plugin_path, json.dumps(config), self.cmd_queue, self.result_queue),
daemon=True
)
self.process.start()
logger.info(f"隔离插件已启动: {plugin_name} (PID={self.process.pid})")
def call(self, method: str, *args, timeout: float = 30):
self.cmd_queue.put({"method": method, "args": args})
try:
result = self.result_queue.get(timeout=timeout)
return result
except:
return {"ok": False, "error": "timeout"}
def shutdown(self):
self.cmd_queue.put("__SHUTDOWN__")
self.process.join(timeout=10)
if self.process.is_alive():
self.process.terminate()
logger.info(f"隔离插件已关闭: {self.name}")
@property
def pid(self):
return self.process.pid if self.process.is_alive() else None