Skip to content

Commit 980c7b4

Browse files
committed
fix: 托盘 watchdog 自动重启消失的托盘进程
托盘进程是 _spawn_tray_process 在 lifespan startup 时拉一次的 DETACHED_PROCESS 子进程。一旦它消失(pystray win32 消息循环退出 / 用户从任务管理器结束 / 偶发崩溃),用户看不到右下角图标就无法管理 gateway。\n\n加一个 daemon 线程 _tray_watchdog:每 5 秒检查 _is_tray_already_running,死了就再拉一个。\n\nkill tray 进程后验证 gateway 自动拉起新的。
1 parent de4cbe4 commit 980c7b4

1 file changed

Lines changed: 34 additions & 0 deletions

File tree

qmt_gateway/__main__.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,36 @@ def _spawn_tray_process(home: Path) -> None:
327327
logger.warning(f"托盘进程拉起失败(不影响 gateway 运行): {exc}")
328328

329329

330+
def _tray_watchdog(home: Path) -> None:
331+
"""监控托盘子进程;死了就拉一个新。
332+
333+
托盘进程是 ``subprocess.Popen`` + ``DETACHED_PROCESS`` 拉起的独立进程,
334+
pystray 的 win32 消息循环退出(菜单 quit、用户从任务管理器结束、
335+
偶发崩溃)后不会自动重启。Uvicorn 主进程继续跑,但用户在右下角
336+
看不到托盘就无法管理 gateway(启动/停止/重启/退出/打开浏览器)。
337+
338+
这个 watchdog 跑在 daemon 线程里,每 5 秒查一次;tray 死了就再拉
339+
一次。极轻量,对 gateway 主循环无影响。
340+
"""
341+
import threading
342+
import time
343+
344+
def _loop() -> None:
345+
while True:
346+
time.sleep(5.0)
347+
try:
348+
if _is_tray_already_running():
349+
continue
350+
logger.warning("检测到托盘进程消失,重新拉起")
351+
_spawn_tray_process(home)
352+
except Exception as exc: # noqa: BLE001
353+
logger.warning(f"托盘 watchdog 异常(将继续下一轮检查): {exc}")
354+
355+
t = threading.Thread(target=_loop, name="tray-watchdog", daemon=True)
356+
t.start()
357+
358+
359+
330360
def main():
331361
"""主入口函数"""
332362
parser = argparse.ArgumentParser(
@@ -414,6 +444,10 @@ def main():
414444
# 此后再 fork 会出问题。注意托盘是 *可选* 的:如果创建失败(比如
415445
# 跑在 Linux/macOS 调试环境、或 pystray 不可用),gateway 仍然要正常服务。
416446
_spawn_tray_process(runtime.home_path)
447+
# 托盘 watchdog:托盘进程可能因为 pystray win32 消息循环退出、用户从
448+
# 任务管理器结束、或者偶发崩溃而消失。watchdog 每 5 秒检查一次,死了
449+
# 就自动再拉一个——保证用户在右下角始终能看到托盘图标管理 gateway。
450+
_tray_watchdog(runtime.home_path)
417451

418452
uvicorn.run(
419453
"qmt_gateway.app:app",

0 commit comments

Comments
 (0)