Files
evotraders/start.sh

326 lines
9.4 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# ============================================================
# 大时代 单机启动脚本
#
# 用法:
# ./start.sh # 构建前端 + 后台启动全部服务 (单机模式)
# ./start.sh --no-build # 跳过前端构建
# ./start.sh --no-daemon # 前台运行 (不使用 nohup)
# ./start.sh stop # 停止所有后台服务
# ./start.sh status # 查看服务状态
#
# 环境变量:
# WORKERS=2 # uvicorn worker 数 (默认: 2)
# GATEWAY_HOST=0.0.0.0 # Gateway 绑定地址
# GATEWAY_PORT=8765 # Gateway 端口
# FRONTEND_PORT=8080 # 前端服务端口 (默认: 8080)
# ============================================================
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "${SCRIPT_DIR}"
WORKERS="${WORKERS:-2}"
GATEWAY_HOST="${GATEWAY_HOST:-0.0.0.0}"
GATEWAY_PORT="${GATEWAY_PORT:-8765}"
FRONTEND_PORT="${FRONTEND_PORT:-8080}"
PID_DIR="${SCRIPT_DIR}/.pids"
LOG_DIR="${SCRIPT_DIR}/logs"
FRONTEND_DIST="${SCRIPT_DIR}/frontend/dist"
DAEMON=true
BUILD_FRONTEND=true
ACTION="start"
for arg in "$@"; do
case "$arg" in
--no-daemon) DAEMON=false ;;
--no-build) BUILD_FRONTEND=false ;;
stop) ACTION="stop" ;;
status) ACTION="status" ;;
*) echo -e "${YELLOW}忽略未知参数: ${arg}${NC}" ;;
esac
done
ensure_dirs() {
mkdir -p "${PID_DIR}" "${LOG_DIR}"
}
save_pid() {
local name="$1" pid="$2"
echo "${pid}" > "${PID_DIR}/${name}.pid"
}
read_pid() {
local name="$1"
local pidfile="${PID_DIR}/${name}.pid"
if [ -f "${pidfile}" ]; then
cat "${pidfile}"
fi
}
is_running() {
local pid="$1"
[ -n "${pid}" ] && kill -0 "${pid}" 2>/dev/null
}
stop_service() {
local name="$1"
local pid
pid="$(read_pid "${name}")"
if is_running "${pid}"; then
echo -e " ${YELLOW}停止${NC} ${name} (PID: ${pid})"
kill "${pid}" 2>/dev/null || true
local count=0
while is_running "${pid}" && [ "${count}" -lt 20 ]; do
sleep 0.5
count=$((count + 1))
done
if is_running "${pid}"; then
echo -e " ${RED}强制终止${NC} ${name}"
kill -9 "${pid}" 2>/dev/null || true
fi
fi
rm -f "${PID_DIR}/${name}.pid"
}
print_status() {
local name="$1" port="$2"
local pid
pid="$(read_pid "${name}")"
if is_running "${pid}"; then
echo -e " ${GREEN}${NC} ${name} (PID: ${pid}, 端口: ${port})"
else
echo -e " ${RED}${NC} ${name} (未运行)"
fi
}
load_env() {
if [ -f .env ]; then
set -a
# shellcheck disable=SC1091
source .env
set +a
else
echo -e "${YELLOW}警告: 未检测到 .env将使用环境变量或默认值${NC}"
fi
}
check_prereqs() {
# Resolve the Python interpreter to use. Prefer the project venv.
if [ -x "${SCRIPT_DIR}/.venv/bin/python" ]; then
PYTHON="${SCRIPT_DIR}/.venv/bin/python"
else
PYTHON="$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true)"
fi
if [ -z "${PYTHON}" ]; then
echo -e "${RED}未找到 python${NC}"
exit 1
fi
echo -e "${GREEN}使用 Python: ${PYTHON}${NC}"
command -v lsof >/dev/null 2>&1 || {
echo -e "${RED}未找到 lsof${NC}"
exit 1
}
}
kill_port() {
local port="$1"
local pids
pids="$(lsof -ti :"${port}" 2>/dev/null || true)"
if [ -n "${pids}" ]; then
echo -e "${YELLOW}端口 ${port} 已被占用,清理 PID:${NC} ${pids}"
echo "${pids}" | xargs kill -9 2>/dev/null || true
sleep 0.5
fi
}
do_stop() {
echo -e "${CYAN}停止所有服务...${NC}"
for svc in frontend agent_service trading_service news_service runtime_service; do
stop_service "${svc}"
done
echo -e "${GREEN}已停止${NC}"
}
do_status() {
echo ""
echo -e "${CYAN}服务状态${NC}"
print_status "agent_service" 8000
print_status "trading_service" 8001
print_status "news_service" 8002
print_status "runtime_service" 8003
print_status "frontend" "${FRONTEND_PORT}"
echo ""
echo -e " ${CYAN}${NC} Gateway 由 runtime_service 管理,运行日志写入 runs/<run_id>/logs/gateway.log"
echo ""
if [ -d "${FRONTEND_DIST}" ]; then
echo -e " ${GREEN}${NC} 前端已构建: ${FRONTEND_DIST}"
else
echo -e " ${YELLOW}${NC} 前端未构建,运行: cd frontend && npm run build"
fi
echo ""
}
build_frontend() {
if ! ${BUILD_FRONTEND}; then
return
fi
echo ""
echo -e "${CYAN}构建前端...${NC}"
if [ -d "frontend" ] && command -v npm >/dev/null 2>&1; then
if [ -f "frontend/package-lock.json" ]; then
(cd frontend && npm ci && npm run build)
else
(cd frontend && npm install && npm run build)
fi
echo -e "${GREEN}前端构建完成: ${FRONTEND_DIST}${NC}"
else
echo -e "${RED}前端构建失败: 需要 npm 和 frontend 目录${NC}"
exit 1
fi
}
start_single_daemon() {
local name="$1" app_path="$2" port="$3"
echo -e " ${GREEN}${NC} ${name} → :${port} (${WORKERS} workers)"
nohup env SERVICE_NAME="${name}" "${PYTHON}" -m uvicorn "${app_path}" \
--host 0.0.0.0 \
--port "${port}" \
--workers "${WORKERS}" \
--log-level warning \
--no-access-log \
>> "${LOG_DIR}/${name}.log" 2>&1 &
save_pid "${name}" $!
}
start_daemon() {
start_single_daemon "agent_service" "backend.apps.agent_service:app" 8000
start_single_daemon "trading_service" "backend.apps.trading_service:app" 8001
start_single_daemon "news_service" "backend.apps.news_service:app" 8002
start_single_daemon "runtime_service" "backend.apps.runtime_service:app" 8003
echo -e " ${GREEN}${NC} frontend → http://0.0.0.0:${FRONTEND_PORT}"
nohup env SERVICE_NAME="frontend" "${PYTHON}" -m uvicorn "backend.apps.frontend_service:app" \
--host 0.0.0.0 \
--port "${FRONTEND_PORT}" \
--workers "${WORKERS}" \
--log-level warning \
--no-access-log \
>> "${LOG_DIR}/frontend.log" 2>&1 &
save_pid "frontend" $!
echo ""
echo -e "${GREEN}所有服务已在后台启动${NC}"
echo " 日志目录: ${LOG_DIR}/"
echo " PID 目录: ${PID_DIR}/"
echo ""
echo " 查看状态: ./start.sh status"
echo " 查看服务日志: tail -f ${LOG_DIR}/runtime_service.log"
echo " 查看运行日志: tail -f runs/<run_id>/logs/gateway.log"
echo " 停止服务: ./start.sh stop"
echo ""
}
PIDS=()
cleanup_foreground() {
echo ""
echo -e "${YELLOW}正在停止所有服务...${NC}"
if [ "${#PIDS[@]}" -gt 0 ]; then
kill "${PIDS[@]}" 2>/dev/null || true
wait "${PIDS[@]}" 2>/dev/null || true
fi
}
start_single_foreground() {
local name="$1" app_path="$2" port="$3"
echo -e " ${GREEN}${NC} ${name} → :${port}"
env SERVICE_NAME="${name}" "${PYTHON}" -m uvicorn "${app_path}" \
--host 0.0.0.0 \
--port "${port}" \
--log-level warning \
--no-access-log &
PIDS+=($!)
}
start_foreground() {
trap cleanup_foreground EXIT INT TERM
start_single_foreground "agent_service" "backend.apps.agent_service:app" 8000
start_single_foreground "trading_service" "backend.apps.trading_service:app" 8001
start_single_foreground "news_service" "backend.apps.news_service:app" 8002
start_single_foreground "runtime_service" "backend.apps.runtime_service:app" 8003
echo -e " ${GREEN}${NC} frontend → http://0.0.0.0:${FRONTEND_PORT}"
env SERVICE_NAME="frontend" "${PYTHON}" -m uvicorn "backend.apps.frontend_service:app" \
--host 0.0.0.0 \
--port "${FRONTEND_PORT}" \
--log-level warning \
--no-access-log &
PIDS+=($!)
echo ""
echo -e "${GREEN}服务以前台模式运行。按 Ctrl+C 停止。${NC}"
wait
}
do_start() {
ensure_dirs
check_prereqs
load_env
export TRADING_SERVICE_URL="${TRADING_SERVICE_URL:-http://localhost:8001}"
export NEWS_SERVICE_URL="${NEWS_SERVICE_URL:-http://localhost:8002}"
export RUNTIME_SERVICE_URL="${RUNTIME_SERVICE_URL:-http://localhost:8003}"
build_frontend
echo ""
echo -e "${CYAN}停止已有服务...${NC}"
for svc in frontend agent_service trading_service news_service runtime_service; do
stop_service "${svc}"
done
echo ""
echo -e "${CYAN}══════════════════════════════════════════${NC}"
echo -e "${CYAN} 大时代 · 单机启动${NC}"
echo -e "${CYAN}══════════════════════════════════════════${NC}"
echo ""
echo -e "${YELLOW}说明:${NC} 当前脚本适合单机运行或演示环境。"
echo -e "${YELLOW}正式生产部署请优先使用 deploy/systemd + nginx 静态前端方案。${NC}"
echo ""
if ${DAEMON}; then
start_daemon
else
start_foreground
fi
}
case "${ACTION}" in
start)
do_start
;;
stop)
do_stop
;;
status)
do_status
;;
*)
echo -e "${RED}未知动作: ${ACTION}${NC}"
exit 1
;;
esac