graceful exit
This commit is contained in:
@@ -1,10 +1,38 @@
|
|||||||
import logging
|
import logging
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from .config import Config
|
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
|
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:
|
def create_app(config_object: type = Config) -> Flask:
|
||||||
app = Flask(__name__, template_folder="templates", static_folder="static")
|
app = Flask(__name__, template_folder="templates", static_folder="static")
|
||||||
@@ -31,4 +59,9 @@ def create_app(config_object: type = Config) -> Flask:
|
|||||||
def healthz():
|
def healthz():
|
||||||
return {"status": "ok"}
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
|
||||||
|
# 绑定退出钩子(开发/简单部署环境很有用)
|
||||||
|
atexit.register(_graceful_shutdown, app, "atexit")
|
||||||
|
_bind_signals(app)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|||||||
@@ -247,4 +247,6 @@ class ChatManager:
|
|||||||
with self._lock:
|
with self._lock:
|
||||||
keys = list(self._procs.keys())
|
keys = list(self._procs.keys())
|
||||||
for k in keys:
|
for k in keys:
|
||||||
self.stop(k)
|
self.stop(k)
|
||||||
|
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@ from flask_pymongo import PyMongo
|
|||||||
from qcloud_cos import CosConfig
|
from qcloud_cos import CosConfig
|
||||||
from qcloud_cos import CosS3Client
|
from qcloud_cos import CosS3Client
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
mongo = PyMongo()
|
mongo = PyMongo()
|
||||||
|
|
||||||
@@ -76,4 +77,15 @@ def register_namespaces(app):
|
|||||||
# 延迟导入以避免循环
|
# 延迟导入以避免循环
|
||||||
from .sockets.namespaces import VSCodeNamespace, AgentNamespace
|
from .sockets.namespaces import VSCodeNamespace, AgentNamespace
|
||||||
socketio.on_namespace(VSCodeNamespace("/vscode"))
|
socketio.on_namespace(VSCodeNamespace("/vscode"))
|
||||||
socketio.on_namespace(AgentNamespace("/agent"))
|
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)
|
||||||
|
|
||||||
Reference in New Issue
Block a user