good version for web

This commit is contained in:
2026-02-18 09:36:18 +08:00
parent 62ea5d36a5
commit 72ab0c0b56
42 changed files with 1305 additions and 1515 deletions

View File

@@ -11,18 +11,41 @@ import psutil
from typing import Dict, Any, Optional
from docker.errors import DockerException, NotFound
# 数据库相关导入
try:
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
DATABASE_AVAILABLE = True
except ImportError:
DATABASE_AVAILABLE = False
class ServiceOrchestrator:
"""服务编排服务"""
def __init__(self, deployment_mode="local"):
def __init__(self, deployment_mode="local", db_url=None):
"""初始化服务编排器
Args:
deployment_mode: 部署模式,支持"docker""local"
db_url: 数据库连接URL用于重新加载服务信息
"""
self.deployment_mode = deployment_mode
self.processes = {} # 存储本地进程信息
self.db_url = db_url
self.db_engine = None
self.db_session = None
# 初始化数据库连接
if db_url and DATABASE_AVAILABLE:
try:
self.db_engine = create_engine(db_url)
self.db_session = sessionmaker(bind=self.db_engine)()
print("数据库连接成功")
except Exception as e:
print(f"数据库连接失败: {e}")
self.db_engine = None
self.db_session = None
if deployment_mode == "docker":
try:
@@ -178,15 +201,17 @@ class ServiceOrchestrator:
# 本地进程启动
if service_id not in self.processes:
# 服务不在进程列表中,可能是服务重启导致的
# 这种情况下,需要从外部重新注册服务
# 暂时返回错误,建议用户重新注册服务
print(f"服务 {service_id} 不在进程列表中,无法启动")
return {
"success": False,
"error": "服务不存在,请重新注册服务",
"service_id": service_id,
"status": "error"
}
# 尝试从数据库重新加载服务信息
print(f"服务 {service_id} 不在进程列表中,尝试从数据库重新加载")
service_info = self.reload_service_from_db(service_id)
if not service_info:
return {
"success": False,
"error": "服务不存在,请重新注册服务",
"service_id": service_id,
"status": "error"
}
process_info = self.processes[service_id]
@@ -209,11 +234,76 @@ class ServiceOrchestrator:
project_info = process_info["project_info"]
service_config = process_info["service_config"]
print(f"准备启动服务 {service_id}")
print(f"服务目录: {service_dir}")
print(f"服务配置: {service_config}")
# 检查服务目录是否存在如果不存在则从Gitea克隆
if not os.path.exists(service_dir):
print(f"服务目录不存在: {service_dir}尝试从Gitea克隆代码")
repository_id = service_config.get("repository_id")
if not repository_id:
return {
"success": False,
"error": "无法获取仓库ID无法克隆代码",
"service_id": service_id,
"status": "error"
}
# 从数据库获取仓库信息
try:
from app.models.database import SessionLocal
from app.models.models import AlgorithmRepository
db = SessionLocal()
repository = db.query(AlgorithmRepository).filter(AlgorithmRepository.id == repository_id).first()
db.close()
if not repository:
return {
"success": False,
"error": f"仓库不存在: {repository_id}",
"service_id": service_id,
"status": "error"
}
# 克隆仓库
from app.gitea.service import GiteaService
gitea_service = GiteaService()
clone_success = gitea_service.clone_repository(
repository.repo_url,
service_id,
repository.branch or "main"
)
if not clone_success:
return {
"success": False,
"error": f"克隆仓库失败: {repository.repo_url}",
"service_id": service_id,
"status": "error"
}
print(f"成功从Gitea克隆仓库到: {service_dir}")
except Exception as e:
print(f"克隆仓库时出错: {str(e)}")
return {
"success": False,
"error": f"克隆仓库时出错: {str(e)}",
"service_id": service_id,
"status": "error"
}
# 启动服务进程
print(f"开始启动服务进程...")
new_process_info = self._start_local_service_process(service_id, service_dir, project_info, service_config)
print(f"服务进程启动完成: {new_process_info}")
# 验证服务启动
print(f"开始验证服务启动...")
if not self._verify_local_service_startup(service_id, service_config):
print(f"服务启动验证失败")
return {
"success": False,
"error": "服务启动验证失败",
@@ -221,6 +311,7 @@ class ServiceOrchestrator:
"status": "error"
}
print(f"服务启动成功!")
return {
"success": True,
"service_id": service_id,
@@ -610,6 +701,76 @@ class ServiceOrchestrator:
"health": "unknown"
}
def reload_service_from_db(self, service_id: str) -> Optional[Dict[str, Any]]:
"""从数据库重新加载服务信息
Args:
service_id: 服务ID
Returns:
服务信息字典如果未找到则返回None
"""
if not self.db_session:
print("数据库连接不可用,无法重新加载服务信息")
return None
try:
# 从数据库查询服务信息
query = text("""
SELECT service_id, api_url, status, config, host, port
FROM algorithm_services
WHERE service_id = :service_id
""")
result = self.db_session.execute(query, {"service_id": service_id}).fetchone()
if not result:
print(f"数据库中未找到服务 {service_id}")
return None
# 构建服务信息字典
# config字段已经是JSON类型不需要再解析
config_data = result[3] if result[3] else {}
# 将host和port添加到config中
config_data["host"] = result[4] if result[4] else "localhost"
config_data["port"] = result[5] if result[5] else 8000
service_info = {
"service_id": result[0],
"api_url": result[1],
"status": result[2],
"config": config_data
}
# 从config中提取容器ID
container_id = service_info["config"].get("container_id")
if container_id:
service_info["container_id"] = container_id
# 更新本地进程缓存(仅用于本地模式)
if self.deployment_mode == "local":
# 从config中获取项目类型如果没有则默认为python
project_type = service_info["config"].get("project_type", "python")
self.processes[service_id] = {
"service_dir": f"/tmp/algorithms/{service_id}",
"project_info": {
"project_type": project_type,
"name": service_info["config"].get("name", ""),
"version": service_info["config"].get("version", "1.0.0"),
"description": service_info["config"].get("description", "")
},
"service_config": service_info["config"],
"pid": None # PID需要重新获取
}
print(f"成功从数据库重新加载服务 {service_id} 的信息")
return service_info
except Exception as e:
print(f"从数据库重新加载服务信息失败: {e}")
return None
def get_service_logs(self, container_id: str, lines: int = 100) -> Dict[str, Any]:
"""获取服务日志
@@ -1429,7 +1590,10 @@ def main(data):
import requests
# 构建健康检查URL
# 使用localhost而不是0.0.0.0,因为健康检查是在本地执行的
host = service_config.get("host", "localhost")
if host == "0.0.0.0":
host = "localhost"
port = service_config.get("port", 8000)
health_check_url = f"http://{host}:{port}/health"
@@ -1437,5 +1601,6 @@ def main(data):
response = requests.get(health_check_url, timeout=10)
return response.status_code == 200
except:
except Exception as e:
print(f"健康检查失败: {e}")
return False