diff --git a/Html/apps/__init__.py b/Html/apps/__init__.py index 00ed968..82edac9 100644 --- a/Html/apps/__init__.py +++ b/Html/apps/__init__.py @@ -1,10 +1,38 @@ import logging from flask import Flask from .config import Config -from .extensions import init_extensions, register_namespaces +from .extensions import init_extensions, register_namespaces, close_extensions from .views import register_blueprints +import atexit +import logging +import signal +import threading +_shutdown_once = threading.Event() +def _graceful_shutdown(app: Flask, signum: str = "atexit"): + # 防止重复执行 + if _shutdown_once.is_set(): + return + _shutdown_once.set() + + app.logger.info("Shutting down gracefully (%s)...", signum) + try: + close_extensions() + finally: + app.logger.info("Cleanup finished (%s).", signum) + +def _bind_signals(app: Flask): + def handler(sig, frame): + _graceful_shutdown(app, signum=sig) + # 不要在这里强行 os._exit(0),让外层管理器决定何时真正退出 + for s in (signal.SIGINT, signal.SIGTERM, getattr(signal, "SIGQUIT", None)): + if s is not None: + try: + signal.signal(s, handler) + except Exception: + # 某些环境不能重绑定(如部分 WSGI 容器内),忽略 + pass def create_app(config_object: type = Config) -> Flask: app = Flask(__name__, template_folder="templates", static_folder="static") @@ -31,4 +59,9 @@ def create_app(config_object: type = Config) -> Flask: def healthz(): return {"status": "ok"} + + # 绑定退出钩子(开发/简单部署环境很有用) + atexit.register(_graceful_shutdown, app, "atexit") + _bind_signals(app) + return app diff --git a/Html/apps/extension_ase/ase_client/manager.py b/Html/apps/extension_ase/ase_client/manager.py index 0841273..466bfd6 100644 --- a/Html/apps/extension_ase/ase_client/manager.py +++ b/Html/apps/extension_ase/ase_client/manager.py @@ -247,4 +247,6 @@ class ChatManager: with self._lock: keys = list(self._procs.keys()) for k in keys: - self.stop(k) \ No newline at end of file + self.stop(k) + + \ No newline at end of file diff --git a/Html/apps/extensions.py b/Html/apps/extensions.py index 2c6aaba..d55cc80 100644 --- a/Html/apps/extensions.py +++ b/Html/apps/extensions.py @@ -9,6 +9,7 @@ from flask_pymongo import PyMongo from qcloud_cos import CosConfig from qcloud_cos import CosS3Client +from typing import Optional mongo = PyMongo() @@ -76,4 +77,15 @@ def register_namespaces(app): # 延迟导入以避免循环 from .sockets.namespaces import VSCodeNamespace, AgentNamespace socketio.on_namespace(VSCodeNamespace("/vscode")) - socketio.on_namespace(AgentNamespace("/agent")) \ No newline at end of file + socketio.on_namespace(AgentNamespace("/agent")) + + +def close_extensions(): + # 按“最可能阻塞”的优先级依次停止 + try: + if user_uuid2chatmanager: + for uuid in user_uuid2chatmanager: + user_uuid2chatmanager[uuid].disconnect(uuid) + except Exception as e: + print("Failed to shutdown scheduler: %s", e) + \ No newline at end of file