Commit 490a3565c1ae56110a58f6c2f70911df6da31667

Authored by 朱焱飞
1 parent 25a8b07f

feat: 集成Nacos服务注册与注销功能,并调整应用入口为启动FastAPI服务。

@@ -18,5 +18,18 @@ MYSQL_DATABASE=fw_pms @@ -18,5 +18,18 @@ MYSQL_DATABASE=fw_pms
18 SCHEDULER_CRON_HOUR=2 18 SCHEDULER_CRON_HOUR=2
19 SCHEDULER_CRON_MINUTE=0 19 SCHEDULER_CRON_MINUTE=0
20 20
  21 +# 服务配置
  22 +SERVER_PORT=8009
  23 +
  24 +# Nacos 配置(本地开发默认关闭)
  25 +NACOS_ENABLED=true
  26 +NACOS_SERVER_ADDR=172.26.154.169:8848
  27 +NACOS_NAMESPACE=0ba6d5d6-2a49-4086-b6c3-dbeec01c61a4
  28 +NACOS_SERVICE_NAME=fw-pms-ai
  29 +NACOS_SERVICE_PORT=8009
  30 +NACOS_METADATA_GROUP=discovery-test-group
  31 +NACOS_METADATA_REGION=test
  32 +NACOS_METADATA_VERSION=1.0
  33 +
21 # 日志配置 34 # 日志配置
22 LOG_LEVEL=INFO 35 LOG_LEVEL=INFO
.gitignore
@@ -67,3 +67,7 @@ logs/ @@ -67,3 +67,7 @@ logs/
67 # Local development 67 # Local development
68 *.local 68 *.local
69 *.bak 69 *.bak
  70 +
  71 +# 环境配置文件(含敏感信息)
  72 +.env
  73 +.env.*
pyproject.toml
@@ -40,6 +40,9 @@ dependencies = [ @@ -40,6 +40,9 @@ dependencies = [
40 # Web API 40 # Web API
41 "fastapi>=0.109.0", 41 "fastapi>=0.109.0",
42 "uvicorn[standard]>=0.27.0", 42 "uvicorn[standard]>=0.27.0",
  43 +
  44 + # Nacos 服务注册
  45 + "nacos-sdk-python>=2.0.0,<3.0.0",
43 ] 46 ]
44 47
45 [project.optional-dependencies] 48 [project.optional-dependencies]
src/fw_pms_ai/api/app.py
@@ -13,6 +13,7 @@ from fastapi.staticfiles import StaticFiles @@ -13,6 +13,7 @@ from fastapi.staticfiles import StaticFiles
13 from fastapi.responses import FileResponse 13 from fastapi.responses import FileResponse
14 14
15 from .routes import tasks 15 from .routes import tasks
  16 +from ..config import register_service, deregister_service
16 17
17 logger = logging.getLogger(__name__) 18 logger = logging.getLogger(__name__)
18 19
@@ -21,7 +22,9 @@ logger = logging.getLogger(__name__) @@ -21,7 +22,9 @@ logger = logging.getLogger(__name__)
21 async def lifespan(app: FastAPI): 22 async def lifespan(app: FastAPI):
22 """应用生命周期管理""" 23 """应用生命周期管理"""
23 logger.info("API 服务启动") 24 logger.info("API 服务启动")
  25 + register_service()
24 yield 26 yield
  27 + deregister_service()
25 logger.info("API 服务关闭") 28 logger.info("API 服务关闭")
26 29
27 30
src/fw_pms_ai/config/__init__.py
1 """配置模块""" 1 """配置模块"""
2 2
3 from .settings import Settings, get_settings 3 from .settings import Settings, get_settings
  4 +from .nacos_client import register_service, deregister_service
4 5
5 -__all__ = ["Settings", "get_settings"] 6 +__all__ = ["Settings", "get_settings", "register_service", "deregister_service"]
src/fw_pms_ai/config/nacos_client.py 0 → 100644
  1 +"""
  2 +Nacos 服务注册客户端
  3 +使用 nacos-sdk-python v2,内置自动心跳
  4 +"""
  5 +
  6 +import logging
  7 +import socket
  8 +
  9 +import nacos
  10 +
  11 +from .settings import get_settings
  12 +
  13 +logger = logging.getLogger(__name__)
  14 +
  15 +_client: nacos.NacosClient | None = None
  16 +
  17 +
  18 +def _get_local_ip() -> str:
  19 + """获取本机内网 IP"""
  20 + try:
  21 + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  22 + s.connect(("8.8.8.8", 80))
  23 + ip = s.getsockname()[0]
  24 + s.close()
  25 + return ip
  26 + except Exception:
  27 + return "127.0.0.1"
  28 +
  29 +
  30 +def register_service() -> None:
  31 + """注册服务到 Nacos(v2 内置自动心跳)"""
  32 + global _client
  33 + settings = get_settings()
  34 +
  35 + if not settings.nacos_enabled:
  36 + logger.info("Nacos 注册已禁用,跳过")
  37 + return
  38 +
  39 + server_addr = settings.nacos_server_addr
  40 + namespace = settings.nacos_namespace or ""
  41 +
  42 + _client = nacos.NacosClient(
  43 + server_addr,
  44 + namespace=namespace,
  45 + )
  46 +
  47 + ip = _get_local_ip()
  48 + port = settings.nacos_service_port
  49 + service_name = settings.nacos_service_name
  50 + group = settings.nacos_group
  51 +
  52 + metadata = {
  53 + "group": settings.nacos_metadata_group,
  54 + "region": settings.nacos_metadata_region,
  55 + "version": settings.nacos_metadata_version,
  56 + "preserved.register.source": "python",
  57 + }
  58 +
  59 + try:
  60 + _client.add_naming_instance(
  61 + service_name,
  62 + ip,
  63 + port,
  64 + group_name=group,
  65 + metadata=metadata,
  66 + enable=True,
  67 + healthy=True,
  68 + ephemeral=True,
  69 + heartbeat_interval=5,
  70 + )
  71 + logger.info(
  72 + f"Nacos 注册成功: {service_name} -> {ip}:{port} "
  73 + f"(namespace={namespace}, group={group}, 心跳间隔=5s)"
  74 + )
  75 + except Exception as e:
  76 + logger.error(f"Nacos 注册失败: {e}", exc_info=True)
  77 +
  78 +
  79 +def deregister_service() -> None:
  80 + """从 Nacos 注销服务"""
  81 + global _client
  82 + settings = get_settings()
  83 +
  84 + if not settings.nacos_enabled or _client is None:
  85 + return
  86 +
  87 + ip = _get_local_ip()
  88 + port = settings.nacos_service_port
  89 + service_name = settings.nacos_service_name
  90 + group = settings.nacos_group
  91 +
  92 + try:
  93 + _client.remove_naming_instance(
  94 + service_name,
  95 + ip,
  96 + port,
  97 + group_name=group,
  98 + )
  99 + logger.info(f"Nacos 注销成功: {service_name} -> {ip}:{port}")
  100 + except Exception as e:
  101 + logger.error(f"Nacos 注销失败: {e}", exc_info=True)
  102 + finally:
  103 + _client = None
src/fw_pms_ai/config/settings.py
@@ -45,6 +45,20 @@ class Settings(BaseSettings): @@ -45,6 +45,20 @@ class Settings(BaseSettings):
45 scheduler_cron_hour: int = 2 45 scheduler_cron_hour: int = 2
46 scheduler_cron_minute: int = 0 46 scheduler_cron_minute: int = 0
47 47
  48 + # 服务配置
  49 + server_port: int = 8009
  50 +
  51 + # Nacos 配置
  52 + nacos_enabled: bool = False
  53 + nacos_server_addr: str = "172.26.154.169:8848"
  54 + nacos_namespace: str = ""
  55 + nacos_group: str = "DEFAULT_GROUP"
  56 + nacos_service_name: str = "fw-pms-ai"
  57 + nacos_service_port: int = 8009
  58 + nacos_metadata_group: str = "discovery-test-group"
  59 + nacos_metadata_region: str = "test"
  60 + nacos_metadata_version: str = "1.0"
  61 +
48 # 日志配置 62 # 日志配置
49 log_level: str = "INFO" 63 log_level: str = "INFO"
50 64
src/fw_pms_ai/main.py
1 """ 1 """
2 fw-pms-ai 主入口 2 fw-pms-ai 主入口
  3 +作为独立服务部署,启动 FastAPI 应用
3 """ 4 """
4 5
5 import logging 6 import logging
6 -import sys  
7 -from pathlib import Path 7 +import uvicorn
8 8
9 -try:  
10 - from .scheduler.tasks import main as scheduler_main  
11 -except ImportError:  
12 - # 直接运行时,添加项目根目录到 sys.path  
13 - project_root = Path(__file__).parent.parent.parent  
14 - if str(project_root) not in sys.path:  
15 - sys.path.insert(0, str(project_root))  
16 - from fw_pms_ai.scheduler.tasks import main as scheduler_main 9 +from .config import get_settings
17 10
18 11
19 def main(): 12 def main():
20 """应用入口""" 13 """应用入口"""
  14 + settings = get_settings()
  15 +
21 logging.basicConfig( 16 logging.basicConfig(
22 - level=logging.INFO, 17 + level=getattr(logging, settings.log_level.upper(), logging.INFO),
23 format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", 18 format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
24 ) 19 )
25 -  
26 - scheduler_main() 20 +
  21 + uvicorn.run(
  22 + "fw_pms_ai.api.app:app",
  23 + host="0.0.0.0",
  24 + port=settings.server_port,
  25 + log_level=settings.log_level.lower(),
  26 + )
27 27
28 28
29 if __name__ == "__main__": 29 if __name__ == "__main__":