Files
chat_app/backend/serv.sh
2025-12-11 03:53:47 +08:00

301 lines
7.2 KiB
Bash
Executable File

#!/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 "$@"