diff --git a/app/chain/system.py b/app/chain/system.py index 29d6a3c7..c309d3a2 100644 --- a/app/chain/system.py +++ b/app/chain/system.py @@ -32,6 +32,8 @@ class SystemChain(ChainBase): """ 重启系统 """ + from app.core.config import global_vars + if channel and userid: self.post_message(Notification(channel=channel, source=source, title="系统正在重启,请耐心等候!", userid=userid)) @@ -40,6 +42,8 @@ class SystemChain(ChainBase): "channel": channel.value, "userid": userid }, self._restart_file) + # 设置停止标志,通知所有模块准备停止 + global_vars.stop_system() # 重启 SystemHelper.restart() diff --git a/app/helper/system.py b/app/helper/system.py index 0852c8f0..f9c76aab 100644 --- a/app/helper/system.py +++ b/app/helper/system.py @@ -1,3 +1,6 @@ +import os +import signal +import time from pathlib import Path from typing import Tuple @@ -41,17 +44,12 @@ class SystemHelper: ) @staticmethod - def restart() -> Tuple[bool, str]: + def _get_container_id() -> str: """ - 执行Docker重启操作 + 获取当前容器ID """ - if not SystemUtils.is_docker(): - return False, "非Docker环境,无法重启!" + container_id = None try: - # 创建 Docker 客户端 - client = docker.DockerClient(base_url=settings.DOCKER_CLIENT_API) - # 获取当前容器的 ID - container_id = None with open("/proc/self/mountinfo", "r") as f: data = f.read() index_resolv_conf = data.find("resolv.conf") @@ -67,11 +65,109 @@ class SystemHelper: data.rfind("/", 0, index_second_slash) + 1 ) container_id = data[index_first_slash:index_second_slash] + except Exception as e: + logger.debug(f"获取容器ID失败: {str(e)}") + return container_id.strip() if container_id else None + + @staticmethod + def _check_restart_policy() -> bool: + """ + 检查当前容器是否配置了自动重启策略 + """ + try: + # 创建 Docker 客户端 + client = docker.DockerClient(base_url=settings.DOCKER_CLIENT_API) + container_id = SystemHelper._get_container_id() + if not container_id: + return False + + # 获取容器信息 + container = client.containers.get(container_id) + restart_policy = container.attrs.get('HostConfig', {}).get('RestartPolicy', {}) + policy_name = restart_policy.get('Name', 'no') + + # 检查是否有有效的重启策略 + auto_restart_policies = ['always', 'unless-stopped', 'on-failure'] + has_restart_policy = policy_name in auto_restart_policies + + logger.info(f"容器重启策略: {policy_name}, 支持自动重启: {has_restart_policy}") + return has_restart_policy + + except Exception as e: + logger.warning(f"检查重启策略失败: {str(e)}") + return False + + @staticmethod + def restart() -> Tuple[bool, str]: + """ + 执行Docker重启操作 + """ + if not SystemUtils.is_docker(): + return False, "非Docker环境,无法重启!" + + try: + # 检查容器是否配置了自动重启策略 + has_restart_policy = SystemHelper._check_restart_policy() + + if has_restart_policy: + # 有重启策略,使用优雅退出方式 + logger.info("检测到容器配置了自动重启策略,使用优雅重启方式...") + # 发送SIGTERM信号给当前进程,触发优雅停止 + os.kill(os.getpid(), signal.SIGTERM) + return True, "" + else: + # 没有重启策略,使用Docker API强制重启 + logger.info("容器未配置自动重启策略,使用Docker API重启...") + return SystemHelper._docker_api_restart() + + except Exception as err: + logger.error(f"重启失败: {str(err)}") + # 降级为Docker API重启 + logger.warning("降级为Docker API重启...") + return SystemHelper._docker_api_restart() + + @staticmethod + def _docker_api_restart() -> Tuple[bool, str]: + """ + 使用Docker API重启容器,并尝试优雅停止 + """ + try: + # 创建 Docker 客户端 + client = docker.DockerClient(base_url=settings.DOCKER_CLIENT_API) + container_id = SystemHelper._get_container_id() if not container_id: return False, "获取容器ID失败!" - # 重启当前容器 - client.containers.get(container_id.strip()).restart() + + container = client.containers.get(container_id) + + # 尝试优雅停止:先发送SIGTERM信号,给容器30秒时间优雅停止 + try: + logger.info("发送SIGTERM信号,尝试优雅停止...") + container.kill(signal='SIGTERM') + + # 等待容器优雅停止,最多等待30秒 + for i in range(30): + container.reload() + if container.status != 'running': + logger.info(f"容器已优雅停止 (耗时 {i+1} 秒)") + break + time.sleep(1) + else: + # 30秒后仍未停止,强制停止 + logger.warning("优雅停止超时,强制停止容器...") + container.kill(signal='SIGKILL') + + except Exception as stop_err: + logger.warning(f"优雅停止失败: {str(stop_err)}") + # 直接重启 + container.restart() + return True, "" + + # 启动容器 + logger.info("启动容器...") + container.start() return True, "" - except Exception as err: - print(str(err)) - return False, f"重启时发生错误:{str(err)}" + + except Exception as docker_err: + print(str(docker_err)) + return False, f"重启时发生错误:{str(docker_err)}" diff --git a/app/main.py b/app/main.py index 5012d410..0aac48f3 100644 --- a/app/main.py +++ b/app/main.py @@ -22,7 +22,7 @@ from app.db.init import init_db, update_db # uvicorn服务 Server = uvicorn.Server(Config(app, host=settings.HOST, port=settings.PORT, reload=settings.DEV, workers=multiprocessing.cpu_count(), - timeout_graceful_shutdown=30)) + timeout_graceful_shutdown=60)) def start_tray():