This commit is contained in:
2026-03-24 18:07:22 +08:00
parent e062368ef2
commit 9a16f738d8
121 changed files with 8904 additions and 3940 deletions
+68 -3
View File
@@ -1,19 +1,83 @@
import errno
import logging
import socket
from contextlib import asynccontextmanager
import uvicorn
from fastapi import FastAPI
try:
import psutil
except Exception: # pragma: no cover - optional runtime dependency for richer diagnostics
psutil = None
from config import Config
from workflows.workflow_manager import WorkflowManager
from services.nacos_service import load_nacos_config, load_service_config, NacosManager
from services.tool_router import ToolRouter
from services.prompt_manager import get_prompt_manager
from services.integrations.nacos_service import load_nacos_config, load_service_config, NacosManager
from services.tools.tool_router import ToolRouter
from services.core.prompt_manager import get_prompt_manager
from api import endpoints
logger = logging.getLogger(__name__)
def _find_listening_process_on_port(port: int) -> dict | None:
if psutil is None:
return None
try:
for conn in psutil.net_connections(kind="tcp"):
local_port = getattr(getattr(conn, "laddr", None), "port", None)
if local_port != port or conn.status != psutil.CONN_LISTEN:
continue
pid = conn.pid
if pid is None:
return {"pid": None, "process_name": None, "cmdline": None}
try:
proc = psutil.Process(pid)
return {
"pid": pid,
"process_name": proc.name(),
"cmdline": " ".join(proc.cmdline()),
}
except Exception:
return {"pid": pid, "process_name": None, "cmdline": None}
except Exception:
return None
return None
def _ensure_port_available(host: str, port: int) -> None:
probe = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
probe.bind((host, port))
except OSError as exc:
if exc.errno == errno.EADDRINUSE or getattr(exc, "winerror", None) == 10048:
process_info = _find_listening_process_on_port(port) or {}
owner_bits = []
if process_info.get("pid") is not None:
owner_bits.append(f"pid={process_info['pid']}")
if process_info.get("process_name"):
owner_bits.append(f"name={process_info['process_name']}")
if process_info.get("cmdline"):
owner_bits.append(f"cmdline={process_info['cmdline']}")
logger.error(
"端口占用,服务无法启动: host=%s port=%s config=%s%s",
host,
port,
Config._get_config_path(),
f" owner=({' | '.join(owner_bits)})" if owner_bits else "",
)
logger.error("请先停止占用该端口的进程,或修改 `config/config.ini` 中 `[app].port`。")
raise SystemExit(1) from exc
raise
finally:
probe.close()
def create_app() -> FastAPI:
"""创建 FastAPI 应用"""
service_config = load_service_config()
@@ -59,6 +123,7 @@ app = create_app()
if __name__ == "__main__":
service_config = load_service_config()
_ensure_port_available(service_config.host, service_config.port)
uvicorn.run(
"server:app",
host=service_config.host,