#!/bin/bash # Unicorn 服务管理脚本 # 优化版本 - 支持完整的服务管理功能,配置完全从 .env 文件读取 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" } # 加载 .env 文件 load_env() { local env_file=".env" if [ -f "$env_file" ]; then set -a source "$env_file" set +a log_info "已加载 $env_file 文件中的环境变量" else log_warning "$env_file 文件不存在,使用默认值或环境变量" fi } # 从环境变量获取配置,提供默认值 init_config() { # 应用配置 APP_NAME=${APP_NAME:-"app_name"} APP_MODULE=${APP_MODULE:-"app.main:app"} APP_DIR=${APP_DIR:-"$(pwd)"} # 服务器配置 APP_HOST=${APP_HOST:-"0.0.0.0"} APP_PORT=${APP_PORT:-8000} APP_WORKERS=${APP_WORKERS:-1} APP_LOG_LEVEL=${APP_LOG_LEVEL:-"info"} # PID 文件路径 PID_FILE="${APP_DIR}/.${APP_NAME}.pid" # 日志文件路径 LOG_DIR="${APP_DIR}/logs" LOG_FILE="${LOG_DIR}/${APP_NAME}.log" log_info "应用名称: $APP_NAME" log_info "应用模块: $APP_MODULE" log_info "应用目录: $APP_DIR" log_info "服务器地址: $APP_HOST" log_info "服务器端口: $APP_PORT" log_info "工作进程数: $APP_WORKERS" log_info "日志级别: $APP_LOG_LEVEL" } # 检查是否在正确的目录 check_directory() { local target_dir="${APP_DIR}" if [ "$(pwd)" != "$target_dir" ]; then log_warning "当前目录不是项目根目录,切换到 $target_dir" cd "$target_dir" || { log_error "无法切换到项目目录: $target_dir" exit 1 } fi } # 检查 uv 是否可用 check_uv() { if ! command -v uv &> /dev/null; then log_error "uv 命令未找到,请确保 uv 已安装" exit 1 fi } # 检查应用模块是否存在 check_app_module() { local module_path=$(echo "$APP_MODULE" | sed 's/:.*//' | sed 's/\./\//g').py if [ ! -f "$module_path" ]; then log_error "应用模块不存在: $module_path" exit 1 fi } # 创建日志目录 setup_logs() { if [ ! -d "$LOG_DIR" ]; then mkdir -p "$LOG_DIR" log_info "创建日志目录: $LOG_DIR" fi } # 启动 Unicorn 服务器 start_server() { log_info "正在启动 $APP_NAME..." # 检查是否已经在运行 if [ -f "$PID_FILE" ]; then local pid=$(cat "$PID_FILE") if kill -0 "$pid" 2>/dev/null; then log_warning "$APP_NAME 已经在运行 (PID: $pid)" return 0 else log_warning "发现旧的 PID 文件,清理中..." rm -f "$PID_FILE" fi fi # 启动服务器 nohup uv run uvicorn \ "$APP_MODULE" \ --host "$APP_HOST" \ --port "$APP_PORT" \ --workers "$APP_WORKERS" \ --log-level "$APP_LOG_LEVEL" \ --access-log \ --reload > "$LOG_FILE" 2>&1 & local server_pid=$! echo $server_pid > "$PID_FILE" sleep 2 if kill -0 "$server_pid" 2>/dev/null; then log_success "$APP_NAME 已启动 (PID: $server_pid)" log_info "日志文件: $LOG_FILE" log_info "访问地址: http://$APP_HOST:$APP_PORT" else log_error "$APP_NAME 启动失败" rm -f "$PID_FILE" exit 1 fi } # 停止 Unicorn 服务器 stop_server() { log_info "正在停止 $APP_NAME..." if [ ! -f "$PID_FILE" ]; then log_warning "PID 文件不存在,$APP_NAME 可能未运行" return 0 fi local pid=$(cat "$PID_FILE") if kill -0 "$pid" 2>/dev/null; then kill -TERM "$pid" sleep 3 if kill -0 "$pid" 2>/dev/null; then log_warning "正常停止失败,尝试强制停止..." kill -KILL "$pid" sleep 1 fi if kill -0 "$pid" 2>/dev/null; then log_error "无法停止进程 $pid" return 1 else rm -f "$PID_FILE" log_success "$APP_NAME 已停止" fi else log_warning "进程 $pid 不存在,清理 PID 文件" rm -f "$PID_FILE" fi } # 重启 Unicorn 服务器 restart_server() { log_info "正在重启 $APP_NAME..." stop_server sleep 2 start_server } # 查看服务器状态 status_server() { if [ -f "$PID_FILE" ]; then local pid=$(cat "$PID_FILE") if kill -0 "$pid" 2>/dev/null; then log_success "$APP_NAME 正在运行 (PID: $pid)" log_info "端口: $APP_PORT" log_info "工作进程: $APP_WORKERS" return 0 else log_warning "PID 文件存在但进程未运行,清理中..." rm -f "$PID_FILE" fi fi log_info "$APP_NAME 未运行" return 1 } # 查看服务器日志 tail_logs() { if [ -f "$LOG_FILE" ]; then log_info "显示日志文件最后 50 行: $LOG_FILE" echo "==========================================" tail -50 "$LOG_FILE" echo "==========================================" else log_warning "日志文件不存在: $LOG_FILE" fi } # 显示帮助信息 show_help() { echo "用法: $0 {start|stop|restart|status|logs|help}" echo "" echo "服务管理命令:" echo " start 启动 $APP_NAME 服务器" echo " stop 停止 $APP_NAME 服务器" echo " restart 重启 $APP_NAME 服务器" echo " status 查看 $APP_NAME 服务器状态" echo " logs 查看 $APP_NAME 服务器日志" echo " help 显示此帮助信息" echo "" echo "环境变量配置:" echo " 所有配置都从 .env 文件读取,包括:" echo " APP_NAME, APP_MODULE, APP_PORT, APP_WORKERS, APP_LOG_LEVEL 等" echo "" echo "示例:" echo " $0 start # 启动服务器" echo " $0 status # 查看状态" } # 主函数 main() { # 加载环境变量 load_env # 初始化配置 init_config # 执行检查 check_directory check_uv check_app_module setup_logs # 处理命令行参数 case "$1" in start) start_server ;; stop) stop_server ;; restart) restart_server ;; status) status_server ;; logs) tail_logs ;; help|--help|-h) show_help ;; *) if [ -z "$1" ]; then # 默认行为:启动服务器(向后兼容) log_warning "未指定命令,默认执行启动操作" log_warning "建议使用: $0 start" start_server else log_error "未知命令: $1" show_help exit 1 fi ;; esac } # 脚本入口 main "$@"