From 490a3565c1ae56110a58f6c2f70911df6da31667 Mon Sep 17 00:00:00 2001 From: zhuYanFei Date: Fri, 27 Feb 2026 15:40:44 +0800 Subject: [PATCH] feat: 集成Nacos服务注册与注销功能,并调整应用入口为启动FastAPI服务。 --- .env | 13 +++++++++++++ .gitignore | 4 ++++ pyproject.toml | 3 +++ src/fw_pms_ai/api/app.py | 3 +++ src/fw_pms_ai/config/__init__.py | 3 ++- src/fw_pms_ai/config/nacos_client.py | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/fw_pms_ai/config/settings.py | 14 ++++++++++++++ src/fw_pms_ai/main.py | 26 +++++++++++++------------- 8 files changed, 155 insertions(+), 14 deletions(-) create mode 100644 src/fw_pms_ai/config/nacos_client.py diff --git a/.env b/.env index 6fdb721..2c70d39 100644 --- a/.env +++ b/.env @@ -18,5 +18,18 @@ MYSQL_DATABASE=fw_pms SCHEDULER_CRON_HOUR=2 SCHEDULER_CRON_MINUTE=0 +# 服务配置 +SERVER_PORT=8009 + +# Nacos 配置(本地开发默认关闭) +NACOS_ENABLED=true +NACOS_SERVER_ADDR=172.26.154.169:8848 +NACOS_NAMESPACE=0ba6d5d6-2a49-4086-b6c3-dbeec01c61a4 +NACOS_SERVICE_NAME=fw-pms-ai +NACOS_SERVICE_PORT=8009 +NACOS_METADATA_GROUP=discovery-test-group +NACOS_METADATA_REGION=test +NACOS_METADATA_VERSION=1.0 + # 日志配置 LOG_LEVEL=INFO diff --git a/.gitignore b/.gitignore index 593a49a..3a87db4 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,7 @@ logs/ # Local development *.local *.bak + +# 环境配置文件(含敏感信息) +.env +.env.* diff --git a/pyproject.toml b/pyproject.toml index 6e5e257..777a80f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,9 @@ dependencies = [ # Web API "fastapi>=0.109.0", "uvicorn[standard]>=0.27.0", + + # Nacos 服务注册 + "nacos-sdk-python>=2.0.0,<3.0.0", ] [project.optional-dependencies] diff --git a/src/fw_pms_ai/api/app.py b/src/fw_pms_ai/api/app.py index 7125c6b..bc7677c 100644 --- a/src/fw_pms_ai/api/app.py +++ b/src/fw_pms_ai/api/app.py @@ -13,6 +13,7 @@ from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from .routes import tasks +from ..config import register_service, deregister_service logger = logging.getLogger(__name__) @@ -21,7 +22,9 @@ logger = logging.getLogger(__name__) async def lifespan(app: FastAPI): """应用生命周期管理""" logger.info("API 服务启动") + register_service() yield + deregister_service() logger.info("API 服务关闭") diff --git a/src/fw_pms_ai/config/__init__.py b/src/fw_pms_ai/config/__init__.py index 890ea8b..109479b 100644 --- a/src/fw_pms_ai/config/__init__.py +++ b/src/fw_pms_ai/config/__init__.py @@ -1,5 +1,6 @@ """配置模块""" from .settings import Settings, get_settings +from .nacos_client import register_service, deregister_service -__all__ = ["Settings", "get_settings"] +__all__ = ["Settings", "get_settings", "register_service", "deregister_service"] diff --git a/src/fw_pms_ai/config/nacos_client.py b/src/fw_pms_ai/config/nacos_client.py new file mode 100644 index 0000000..92bdef4 --- /dev/null +++ b/src/fw_pms_ai/config/nacos_client.py @@ -0,0 +1,103 @@ +""" +Nacos 服务注册客户端 +使用 nacos-sdk-python v2,内置自动心跳 +""" + +import logging +import socket + +import nacos + +from .settings import get_settings + +logger = logging.getLogger(__name__) + +_client: nacos.NacosClient | None = None + + +def _get_local_ip() -> str: + """获取本机内网 IP""" + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + ip = s.getsockname()[0] + s.close() + return ip + except Exception: + return "127.0.0.1" + + +def register_service() -> None: + """注册服务到 Nacos(v2 内置自动心跳)""" + global _client + settings = get_settings() + + if not settings.nacos_enabled: + logger.info("Nacos 注册已禁用,跳过") + return + + server_addr = settings.nacos_server_addr + namespace = settings.nacos_namespace or "" + + _client = nacos.NacosClient( + server_addr, + namespace=namespace, + ) + + ip = _get_local_ip() + port = settings.nacos_service_port + service_name = settings.nacos_service_name + group = settings.nacos_group + + metadata = { + "group": settings.nacos_metadata_group, + "region": settings.nacos_metadata_region, + "version": settings.nacos_metadata_version, + "preserved.register.source": "python", + } + + try: + _client.add_naming_instance( + service_name, + ip, + port, + group_name=group, + metadata=metadata, + enable=True, + healthy=True, + ephemeral=True, + heartbeat_interval=5, + ) + logger.info( + f"Nacos 注册成功: {service_name} -> {ip}:{port} " + f"(namespace={namespace}, group={group}, 心跳间隔=5s)" + ) + except Exception as e: + logger.error(f"Nacos 注册失败: {e}", exc_info=True) + + +def deregister_service() -> None: + """从 Nacos 注销服务""" + global _client + settings = get_settings() + + if not settings.nacos_enabled or _client is None: + return + + ip = _get_local_ip() + port = settings.nacos_service_port + service_name = settings.nacos_service_name + group = settings.nacos_group + + try: + _client.remove_naming_instance( + service_name, + ip, + port, + group_name=group, + ) + logger.info(f"Nacos 注销成功: {service_name} -> {ip}:{port}") + except Exception as e: + logger.error(f"Nacos 注销失败: {e}", exc_info=True) + finally: + _client = None diff --git a/src/fw_pms_ai/config/settings.py b/src/fw_pms_ai/config/settings.py index 8b40431..dd407d6 100644 --- a/src/fw_pms_ai/config/settings.py +++ b/src/fw_pms_ai/config/settings.py @@ -45,6 +45,20 @@ class Settings(BaseSettings): scheduler_cron_hour: int = 2 scheduler_cron_minute: int = 0 + # 服务配置 + server_port: int = 8009 + + # Nacos 配置 + nacos_enabled: bool = False + nacos_server_addr: str = "172.26.154.169:8848" + nacos_namespace: str = "" + nacos_group: str = "DEFAULT_GROUP" + nacos_service_name: str = "fw-pms-ai" + nacos_service_port: int = 8009 + nacos_metadata_group: str = "discovery-test-group" + nacos_metadata_region: str = "test" + nacos_metadata_version: str = "1.0" + # 日志配置 log_level: str = "INFO" diff --git a/src/fw_pms_ai/main.py b/src/fw_pms_ai/main.py index c752187..a64bf21 100644 --- a/src/fw_pms_ai/main.py +++ b/src/fw_pms_ai/main.py @@ -1,29 +1,29 @@ """ fw-pms-ai 主入口 +作为独立服务部署,启动 FastAPI 应用 """ import logging -import sys -from pathlib import Path +import uvicorn -try: - from .scheduler.tasks import main as scheduler_main -except ImportError: - # 直接运行时,添加项目根目录到 sys.path - project_root = Path(__file__).parent.parent.parent - if str(project_root) not in sys.path: - sys.path.insert(0, str(project_root)) - from fw_pms_ai.scheduler.tasks import main as scheduler_main +from .config import get_settings def main(): """应用入口""" + settings = get_settings() + logging.basicConfig( - level=logging.INFO, + level=getattr(logging, settings.log_level.upper(), logging.INFO), format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", ) - - scheduler_main() + + uvicorn.run( + "fw_pms_ai.api.app:app", + host="0.0.0.0", + port=settings.server_port, + log_level=settings.log_level.lower(), + ) if __name__ == "__main__": -- libgit2 0.22.2