#!/bin/bash # 即梦 Free API 多实例 PM2 管理脚本 # 使用方法: ./multi-instance.sh [start|stop|restart|status|logs] [port1,port2,port3|all] set -e # 颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 日志函数 log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } # 可用的端口列表 AVAILABLE_PORTS=(3302 3303 3304) ALL_APPS=("jimeng-api-3302" "jimeng-api-3303" "jimeng-api-3304") # 检查PM2 check_pm2() { if ! command -v pm2 &> /dev/null; then log_error "PM2 未安装,请先安装 PM2" log_info "npm install -g pm2" exit 1 fi } # 构建项目 build_project() { if [ ! -d "dist" ]; then log_info "构建项目..." yarn install yarn run build log_success "项目构建完成" fi } # 解析端口参数 parse_ports() { local port_arg=$1 local target_ports=() if [ "$port_arg" = "all" ] || [ -z "$port_arg" ]; then target_ports=("${AVAILABLE_PORTS[@]}") else IFS=',' read -ra PORTS <<< "$port_arg" for port in "${PORTS[@]}"; do if [[ " ${AVAILABLE_PORTS[@]} " =~ " ${port} " ]]; then target_ports+=("$port") else log_warning "无效端口: $port (可用端口: ${AVAILABLE_PORTS[*]})" fi done fi echo "${target_ports[@]}" } # 获取应用名称 get_app_name() { local port=$1 echo "jimeng-api-${port}" } # 启动指定端口的服务 start_services() { local ports_str=$1 local env_mode=${2:-production} local target_ports=($(parse_ports "$ports_str")) if [ ${#target_ports[@]} -eq 0 ]; then log_error "没有有效的端口" return 1 fi log_info "启动服务实例..." build_project for port in "${target_ports[@]}"; do local app_name=$(get_app_name "$port") log_info "启动 $app_name (端口 $port)..." # 停止现有实例 pm2 delete "$app_name" 2>/dev/null || true # 启动新实例 if [ "$env_mode" = "production" ]; then pm2 start ecosystem.config.json --only "$app_name" --env production else pm2 start ecosystem.config.json --only "$app_name" fi # 等待启动完成 sleep 2 # 检查启动状态 if pm2 list | grep -q "$app_name.*online"; then log_success "$app_name 启动成功 (http://localhost:$port)" else log_error "$app_name 启动失败" fi done pm2 save log_success "所有服务启动完成" } # 停止指定端口的服务 stop_services() { local ports_str=$1 local target_ports=($(parse_ports "$ports_str")) if [ ${#target_ports[@]} -eq 0 ]; then log_error "没有有效的端口" return 1 fi log_info "停止服务实例..." for port in "${target_ports[@]}"; do local app_name=$(get_app_name "$port") log_info "停止 $app_name..." pm2 delete "$app_name" 2>/dev/null || log_warning "$app_name 不存在或已停止" done log_success "服务停止完成" } # 重启服务 restart_services() { local ports_str=$1 local env_mode=${2:-production} log_info "重启服务..." stop_services "$ports_str" sleep 2 start_services "$ports_str" "$env_mode" } # 查看服务状态 check_status() { local ports_str=$1 local target_ports=($(parse_ports "$ports_str")) echo "=== PM2 进程列表 ===" pm2 list echo echo "=== 服务详细状态 ===" if [ ${#target_ports[@]} -eq 0 ]; then target_ports=("${AVAILABLE_PORTS[@]}") fi for port in "${target_ports[@]}"; do local app_name=$(get_app_name "$port") echo "--- $app_name (端口 $port) ---" if pm2 list | grep -q "$app_name"; then local status=$(pm2 list | grep "$app_name" | awk '{print $10}')\n log_info "状态: $status" # 健康检查 if curl -s "http://localhost:$port/ping" > /dev/null 2>&1; then log_success "健康检查: 正常" # 获取服务器信息 local server_info=$(curl -s "http://localhost:$port/api/servers/current" 2>/dev/null || echo "{}")\n if [ "$server_info" != "{}" ]; then log_info "服务器信息: $(echo "$server_info" | jq -r '.data.server_id // "unknown"' 2>/dev/null || echo "获取失败")" fi else log_error "健康检查: 失败" fi else log_warning "进程不存在" fi echo done echo "=== 端口占用情况 ===" for port in "${target_ports[@]}"; do if netstat -tlnp 2>/dev/null | grep ":$port " > /dev/null; then log_success "端口 $port: 已占用" else log_warning "端口 $port: 未占用" fi done } # 查看日志 view_logs() { local ports_str=$1 local target_ports=($(parse_ports "$ports_str")) if [ ${#target_ports[@]} -eq 0 ]; then log_error "没有有效的端口" return 1 fi if [ ${#target_ports[@]} -eq 1 ]; then # 单个服务,显示实时日志 local app_name=$(get_app_name "${target_ports[0]}") log_info "查看 $app_name 实时日志..." pm2 logs "$app_name" --lines 50 else # 多个服务,显示最近日志 for port in "${target_ports[@]}"; do local app_name=$(get_app_name "$port") echo "=== $app_name 最近日志 ===" pm2 logs "$app_name" --lines 20 --nostream echo done fi } # 显示帮助 show_help() { echo "即梦 Free API 多实例 PM2 管理脚本" echo echo "使用方法:" echo " $0 [ports] [env]" echo echo "命令:" echo " start 启动服务实例" echo " stop 停止服务实例" echo " restart 重启服务实例" echo " status 查看服务状态" echo " logs 查看服务日志" echo " help 显示此帮助信息" echo echo "端口参数:" echo " all 所有端口 (默认)" echo " 3302 仅端口 3302" echo " 3302,3303 多个端口,用逗号分隔" echo " 可用端口: ${AVAILABLE_PORTS[*]}" echo echo "环境参数 (仅 start/restart):" echo " production 生产环境 (默认)" echo " development 开发环境" echo echo "示例:" echo " $0 start all production # 启动所有实例 (生产环境)" echo " $0 start 3302,3303 development # 启动端口 3302 和 3303 (开发环境)" echo " $0 stop 3304 # 停止端口 3304" echo " $0 restart all # 重启所有实例" echo " $0 status # 查看所有实例状态" echo " $0 logs 3302 # 查看端口 3302 的日志" echo echo "服务访问地址:" for port in "${AVAILABLE_PORTS[@]}"; do echo " - http://localhost:$port (jimeng-api-$port)" done } # 主函数 main() { local command="$1" local ports="$2" local env_mode="${3:-production}" check_pm2 case "$command" in start) start_services "$ports" "$env_mode" ;; stop) stop_services "$ports" ;; restart) restart_services "$ports" "$env_mode" ;; status) check_status "$ports" ;; logs) view_logs "$ports" ;; help|--help|-h) show_help ;; "") log_error "请指定命令" show_help exit 1 ;; *) log_error "未知命令: $command" show_help exit 1 ;; esac } # 执行主函数 main "$@"