From 23a0022d186049f130dc27f5904de532a49d7789 Mon Sep 17 00:00:00 2001 From: Kris <2893855659@qq.com> Date: Thu, 25 Jun 2026 16:56:19 +0800 Subject: [PATCH] feat(external_systems): add graceful resource cleanup on app shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增外部系统模块的优雅关停逻辑,在应用关闭时释放所有连接池资源和适配器实例缓存,确保httpx、gRPC等连接能够正常发送FIN包完成关闭流程,而非依赖操作系统强制回收文件描述符。 --- .../package/yuxi/external_systems/__init__.py | 27 +++++++++++++++++++ .../framework/runtime/pool_manager.py | 12 +++++++++ 2 files changed, 39 insertions(+) diff --git a/backend/package/yuxi/external_systems/__init__.py b/backend/package/yuxi/external_systems/__init__.py index b8d6cd41..8f0bd85b 100644 --- a/backend/package/yuxi/external_systems/__init__.py +++ b/backend/package/yuxi/external_systems/__init__.py @@ -12,6 +12,8 @@ from __future__ import annotations +from yuxi.utils import logger + def register_adapters() -> None: """显式注册所有外部系统适配器、认证插件与集成包。 @@ -40,3 +42,28 @@ def register_adapters() -> None: from yuxi.external_systems.integrations.discovery import discover_integration_packages discover_integration_packages() + + +async def shutdown_external_systems() -> None: + """应用关闭时释放外部系统模块持有的资源。 + + 与 ``register_adapters`` 对称,由 ``server/utils/lifespan.py`` 在应用关停时调用。 + + 释放顺序: + 1. ``pool_manager.close_all()``:释放所有连接池句柄(httpx/gRPC 等) + 2. ``default_registry.aclose_all()``:释放适配器实例缓存资源 + + 任一步失败仅记录日志,不中断后续清理(best-effort)。 + """ + from yuxi.external_systems.framework.runtime.pool_manager import pool_manager + from yuxi.external_systems.framework.registry.adapter_registry import default_registry + + try: + await pool_manager.close_all() + except Exception: + logger.warning("释放外部系统连接池失败", exc_info=True) + + try: + await default_registry.aclose_all() + except Exception: + logger.warning("释放外部系统适配器实例失败", exc_info=True) diff --git a/backend/package/yuxi/external_systems/framework/runtime/pool_manager.py b/backend/package/yuxi/external_systems/framework/runtime/pool_manager.py index 6ed22b00..80da6a2f 100644 --- a/backend/package/yuxi/external_systems/framework/runtime/pool_manager.py +++ b/backend/package/yuxi/external_systems/framework/runtime/pool_manager.py @@ -57,6 +57,18 @@ class PoolManagerImpl(PoolManagerPort): for handle in pool.values(): await self._close_handle(handle) + async def close_all(self) -> None: + """释放所有系统所有环境的连接资源。 + + 应用关闭时由 ``shutdown_external_systems`` 调用,确保 httpx/gRPC 等 + 连接句柄优雅关闭(发送 FIN 而非依赖 OS 强制回收 FD)。 + """ + async with self._lock: + for key in list(self._pools.keys()): + pool = self._pools.pop(key) + for handle in pool.values(): + await self._close_handle(handle) + async def _close_handle(self, handle: Any) -> None: """关闭单个连接句柄(静默吞错)。""" try: