#!/usr/bin/env bash # # 大时代 Development Startup Script # ================================ # # 启动模式说明: # ------------- # 本脚本支持两种启动模式: # # 1. 微服务模式 (默认) - 启动 4 个独立服务 + Gateway # 这是推荐的开发模式,各服务独立运行,便于单独调试和重启 # - agent_service (端口 8000): Agent 生命周期管理 # - runtime_service (端口 8003): 运行时配置和 Pipeline 执行 # - trading_service (端口 8001): 市场数据和交易操作 # - news_service (端口 8002): 新闻采集和富化 # - gateway (端口 8765): WebSocket 网关,前端连接入口 # # 2. 独立模式 (--standalone) - 仅启动 Gateway # Gateway 内部会自行管理服务,适合快速验证或资源受限环境 # # 用法: # ./start-dev.sh # 启动微服务模式 # ./start-dev.sh --standalone # 启动独立模式 # ./start-dev.sh --help # 显示帮助信息 # set -euo pipefail # ============================================ # 配置与常量 # ============================================ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly SCRIPT_NAME="$(basename "$0")" # 服务端点配置 readonly AGENT_SERVICE_PORT=8000 readonly TRADING_SERVICE_PORT=8001 readonly NEWS_SERVICE_PORT=8002 readonly RUNTIME_SERVICE_PORT=8003 readonly GATEWAY_PORT=8765 # 颜色定义 readonly RED='\033[0;31m' readonly GREEN='\033[0;32m' readonly YELLOW='\033[1;33m' readonly BLUE='\033[0;34m' readonly CYAN='\033[0;36m' readonly NC='\033[0m' # No Color # 进程 ID 数组 PIDS=() # 启动模式: "microservices" 或 "standalone" MODE="microservices" # ============================================ # 工具函数 # ============================================ log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } log_step() { echo -e "${CYAN}[STEP]${NC} $1" } log_debug() { echo -e "${BLUE}[DEBUG]${NC} $1" } # ============================================ # 帮助信息 # ============================================ show_help() { cat << 'EOF' 大时代 Development Startup Script 用法: ./start-dev.sh [选项] 选项: --standalone 以独立模式启动(仅启动 Gateway,内部管理服务) --help, -h 显示此帮助信息 模式说明: 微服务模式 (默认): 启动 4 个独立微服务 + Gateway,各服务独立进程,便于单独调试 - agent_service: http://localhost:8000 (Agent 生命周期) - trading_service: http://localhost:8001 (市场数据) - news_service: http://localhost:8002 (新闻服务) - runtime_service: http://localhost:8003 (运行时管理) - gateway: ws://localhost:8765 (WebSocket 网关) 独立模式 (--standalone): 仅启动 Gateway,由 Gateway 内部自行管理服务 适合快速验证或资源受限环境 环境要求: - Python 3.9+ - 虚拟环境 (推荐 .venv) - .env 文件 (可选但推荐) 示例: ./start-dev.sh # 启动微服务模式 ./start-dev.sh --standalone # 启动独立模式 EOF } # ============================================ # 参数解析 # ============================================ parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --standalone) MODE="standalone" shift ;; --help|-h) show_help exit 0 ;; *) log_warn "未知选项: $1" log_info "使用 --help 查看帮助信息" exit 1 ;; esac done } # ============================================ # 启动前检查 # ============================================ check_python_version() { log_step "检查 Python 版本..." if ! command -v python >/dev/null 2>&1; then log_error "未找到 python 命令" exit 1 fi local python_version python_version=$(python --version 2>&1 | awk '{print $2}') log_debug "Python 版本: $python_version" python - <<'PY' || { import sys if sys.version_info < (3, 9): print(f"Python 3.9+ 是必需的,当前版本: {sys.version}") sys.exit(1) print(f"Python 版本检查通过: {sys.version.split()[0]}") PY log_error "Python 版本不符合要求 (需要 3.9+)" exit 1 } } check_virtual_env() { log_step "检查虚拟环境..." if [[ -n "${VIRTUAL_ENV:-}" ]]; then log_info "已激活虚拟环境: $VIRTUAL_ENV" return 0 fi if [[ -f "$SCRIPT_DIR/.venv/bin/activate" ]]; then log_warn "未激活虚拟环境,自动激活 .venv" # shellcheck disable=SC1091 source "$SCRIPT_DIR/.venv/bin/activate" log_info "虚拟环境已激活: $VIRTUAL_ENV" else log_warn "未找到虚拟环境,使用系统 Python" fi } check_required_commands() { log_step "检查必要命令..." local missing=() if ! command -v python >/dev/null 2>&1; then missing+=("python") fi if ! command -v lsof >/dev/null 2>&1; then missing+=("lsof") fi if [[ ${#missing[@]} -gt 0 ]]; then log_error "缺少必要命令: ${missing[*]}" log_info "请安装缺失的命令后重试" exit 1 fi log_info "所有必要命令已安装" } check_python_modules() { log_step "检查 Python 依赖模块..." local modules=("fastapi" "uvicorn" "websockets" "yaml" "dotenv") local missing=() for module in "${modules[@]}"; do if ! python -c "import $module" 2>/dev/null; then missing+=("$module") fi done if [[ ${#missing[@]} -gt 0 ]]; then log_error "缺少 Python 模块: ${missing[*]}" log_info "请安装依赖: uv pip install -e '.[dev]' 或 pip install -r requirements.txt" exit 1 fi log_info "所有依赖模块已安装" } check_env_file() { log_step "检查环境配置文件..." if [[ -f "$SCRIPT_DIR/.env" ]]; then log_info "加载环境变量: .env" set -a # shellcheck disable=SC1091 source "$SCRIPT_DIR/.env" set +a else log_warn ".env 文件不存在,使用默认配置" log_info "提示: 复制 env.template 到 .env 并配置您的 API 密钥" fi } check_ports() { log_step "检查端口占用情况..." local ports=() if [[ "$MODE" == "microservices" ]]; then ports=($AGENT_SERVICE_PORT $TRADING_SERVICE_PORT $NEWS_SERVICE_PORT $RUNTIME_SERVICE_PORT $GATEWAY_PORT) else ports=($GATEWAY_PORT) fi local occupied=() for port in "${ports[@]}"; do if lsof -Pi :"$port" -sTCP:LISTEN -t >/dev/null 2>&1; then occupied+=("$port") fi done if [[ ${#occupied[@]} -gt 0 ]]; then log_warn "以下端口已被占用: ${occupied[*]}" log_info "尝试释放端口..." for port in "${occupied[@]}"; do kill_port "$port" done else log_info "所有端口可用" fi } kill_port() { local port="$1" local pids pids=$(lsof -ti :"$port" 2>/dev/null || true) if [[ -n "$pids" ]]; then log_warn "释放端口 $port (PID: $pids)" echo "$pids" | xargs kill -9 2>/dev/null || true sleep 0.5 fi } check_optional_services() { log_step "检查可选服务..." # 检查 npm(用于前端) if ! command -v npm >/dev/null 2>&1; then log_warn "npm 未安装,前端启动功能不可用" else log_info "npm 已安装" fi # 检查 OpenClaw gateway check_openclaw_gateway } check_openclaw_gateway() { local target_host="127.0.0.1" local target_port="18789" if python - </dev/null 2>&1; then import socket sock = socket.socket() sock.settimeout(1.0) sock.connect(("${target_host}", ${target_port})) sock.close() PY log_info "OpenClaw gateway 可连接: ws://${target_host}:${target_port}" else log_warn "OpenClaw gateway 未启动: ws://${target_host}:${target_port}" log_info " OpenClaw 面板功能将不可用" fi } # ============================================ # 服务启动函数 # ============================================ start_service() { local name="$1" local app_path="$2" local port="$3" log_info "启动 ${name} (端口 ${port})..." SERVICE_NAME="${name}" python -m uvicorn "${app_path}" \ --host 0.0.0.0 \ --port "${port}" \ --reload \ --reload-dir backend \ --log-level warning \ --no-access-log & PIDS+=($!) } start_gateway() { log_step "启动 Gateway (WebSocket 服务)..." log_info "Gateway 将作为子进程启动 (端口 ${GATEWAY_PORT})" log_info "前端连接地址: ws://localhost:${GATEWAY_PORT}" SERVICE_NAME="gateway" python -m backend.main \ --mode live \ --host 0.0.0.0 \ --port "$GATEWAY_PORT" & PIDS+=($!) } # ============================================ # 微服务模式启动 # ============================================ start_microservices_mode() { log_step "启动微服务模式..." echo "" echo -e "${CYAN}==========================================${NC}" echo -e "${CYAN} 服务端点 ${NC}" echo -e "${CYAN}==========================================${NC}" echo -e " agent_service: http://localhost:${AGENT_SERVICE_PORT}" echo -e " runtime_service: http://localhost:${RUNTIME_SERVICE_PORT}" echo -e " trading_service: http://localhost:${TRADING_SERVICE_PORT}" echo -e " news_service: http://localhost:${NEWS_SERVICE_PORT}" echo -e " gateway: ws://localhost:${GATEWAY_PORT}" echo -e "${CYAN}==========================================${NC}" echo "" # 设置服务 URL 环境变量 export TRADING_SERVICE_URL="${TRADING_SERVICE_URL:-http://localhost:${TRADING_SERVICE_PORT}}" export NEWS_SERVICE_URL="${NEWS_SERVICE_URL:-http://localhost:${NEWS_SERVICE_PORT}}" export RUNTIME_SERVICE_URL="${RUNTIME_SERVICE_URL:-http://localhost:${RUNTIME_SERVICE_PORT}}" export OPENCLAW_SERVICE_URL="${OPENCLAW_SERVICE_URL:-http://localhost:18789}" export ENABLE_DASHBOARD_COMPAT_EXPORTS="${ENABLE_DASHBOARD_COMPAT_EXPORTS:-true}" log_debug "环境变量:" log_debug " TRADING_SERVICE_URL=${TRADING_SERVICE_URL}" log_debug " NEWS_SERVICE_URL=${NEWS_SERVICE_URL}" log_debug " RUNTIME_SERVICE_URL=${RUNTIME_SERVICE_URL}" log_debug " OPENCLAW_SERVICE_URL=${OPENCLAW_SERVICE_URL}" echo "" # 启动 4 个微服务 start_service "agent_service" "backend.apps.agent_service:app" "$AGENT_SERVICE_PORT" start_service "runtime_service" "backend.apps.runtime_service:app" "$RUNTIME_SERVICE_PORT" start_service "trading_service" "backend.apps.trading_service:app" "$TRADING_SERVICE_PORT" start_service "news_service" "backend.apps.news_service:app" "$NEWS_SERVICE_PORT" # 启动 Gateway(作为子进程) start_gateway echo "" log_info "所有服务已启动" log_info "按 Ctrl+C 停止所有服务" echo "" } # ============================================ # 独立模式启动 # ============================================ start_standalone_mode() { log_step "启动独立模式..." echo "" echo -e "${CYAN}==========================================${NC}" echo -e "${CYAN} 独立模式 ${NC}" echo -e "${CYAN}==========================================${NC}" echo -e " gateway: ws://localhost:${GATEWAY_PORT}" echo -e "${CYAN}==========================================${NC}" echo "" log_info "Gateway 将内部管理服务" # 启动 Gateway(独立模式) start_gateway echo "" log_info "Gateway 已启动(独立模式)" log_info "按 Ctrl+C 停止服务" echo "" } # ============================================ # 清理与信号处理 # ============================================ cleanup() { if [[ ${#PIDS[@]} -gt 0 ]]; then echo "" log_step "正在停止服务..." for pid in "${PIDS[@]}"; do if kill -0 "$pid" 2>/dev/null; then kill "$pid" 2>/dev/null || true fi done # 等待进程结束 wait "${PIDS[@]}" 2>/dev/null || true log_info "所有服务已停止" fi } trap cleanup EXIT INT TERM # ============================================ # 主程序 # ============================================ main() { # 解析命令行参数 parse_args "$@" # 显示启动横幅 echo "" echo -e "${CYAN}==========================================${NC}" echo -e "${CYAN} 大时代 Development Environment ${NC}" echo -e "${CYAN}==========================================${NC}" echo "" # 切换到项目根目录 cd "$SCRIPT_DIR" log_debug "工作目录: $SCRIPT_DIR" # 启动前检查 check_required_commands check_python_version check_virtual_env check_python_modules check_env_file check_ports check_optional_services echo "" echo -e "${GREEN}==========================================${NC}" echo -e "${GREEN} 启动前检查完成 ${NC}" echo -e "${GREEN}==========================================${NC}" echo "" # 根据模式启动服务 if [[ "$MODE" == "standalone" ]]; then start_standalone_mode else start_microservices_mode fi # 等待所有后台进程 wait } # 执行主程序 main "$@"