Files
nxxmdata/bank-backend/node_manager.sh
2025-11-18 10:34:38 +08:00

375 lines
12 KiB
Bash
Raw Permalink 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.

#!/bin/bash
# Node.js 服务管理脚本 - 银行端后端bank-backend
# 使用方法: ./node_manager.sh [start|stop|restart|status]
# 配置区域
APP_NAME="bank-backend"
ENTRY_FILE="server.js"
APP_PORT="5351"
LOG_DIR="logs"
LOG_FILE="${LOG_DIR}/${APP_NAME}.log"
PID_FILE="pid.${APP_NAME}"
NODE_ENV="production"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 创建日志目录
if [ ! -d "$LOG_DIR" ]; then
mkdir -p "$LOG_DIR"
echo "已创建日志目录: $LOG_DIR"
fi
# 检查 Node.js 是否安装
if ! command -v node &> /dev/null; then
echo -e "${RED}错误: Node.js 未安装或不在 PATH 中${NC}"
exit 1
fi
# 检查入口文件是否存在
if [ ! -f "$ENTRY_FILE" ]; then
echo -e "${RED}错误: 入口文件 $ENTRY_FILE 不存在${NC}"
exit 1
fi
# 显示 Node.js 版本信息
NODE_VERSION=$(node --version)
echo -e "${GREEN}Node.js 版本: $NODE_VERSION${NC}"
# 使用 PM2 启动服务(如已安装)
function startPM2() {
if command -v pm2 &> /dev/null; then
echo -e "${YELLOW}检测到已安装 PM2使用 PM2 管理服务: $APP_NAME${NC}"
# 确保日志目录存在
[ -d "$LOG_DIR" ] || mkdir -p "$LOG_DIR"
pm2 start "$ENTRY_FILE" \
--name "$APP_NAME" \
--cwd "$(pwd)" \
--env "$NODE_ENV" \
--time \
--output "$LOG_DIR/${APP_NAME}.out.log" \
--error "$LOG_DIR/${APP_NAME}.err.log"
pm2 save
echo -e "${GREEN}PM2 已启动并保存进程列表pm2 save${NC}"
echo "如需开机自启,请执行(一次性):"
echo "pm2 startup systemd -u $(whoami) --hp $HOME; pm2 save"
echo "查看状态: pm2 status $APP_NAME"
return 0
else
return 1
fi
}
# 停止服务函数
function stopApp() {
echo -e "${YELLOW}正在停止服务: $APP_NAME${NC}"
# 从 PID 文件读取进程 ID
if [ -f "$PID_FILE" ]; then
PID=$(cat "$PID_FILE")
if ps -p $PID > /dev/null 2>&1; then
echo "找到进程 PID: $PID,正在停止..."
kill -TERM $PID
# 等待进程优雅退出
for i in {1..10}; do
if ! ps -p $PID > /dev/null 2>&1; then
echo -e "${GREEN}服务已优雅停止${NC}"
rm -f "$PID_FILE"
return 0
fi
sleep 1
done
# 如果优雅停止失败,强制杀死
echo -e "${YELLOW}优雅停止失败,强制终止进程${NC}"
kill -9 $PID
rm -f "$PID_FILE"
else
echo "PID 文件存在但进程不存在,清理 PID 文件"
rm -f "$PID_FILE"
fi
else
# 如果没有 PID 文件,尝试通过端口查找
PID=$(lsof -ti:$APP_PORT 2>/dev/null)
if [ -n "$PID" ]; then
echo "通过端口 $APP_PORT 找到 PID: $PID,正在停止..."
kill -TERM $PID
sleep 2
if ps -p $PID > /dev/null 2>&1; then
kill -9 $PID
fi
echo -e "${GREEN}服务已停止${NC}"
else
echo -e "${YELLOW}未找到运行中的服务: $APP_NAME${NC}"
fi
fi
}
# 启动服务函数
function startApp() {
echo -e "${YELLOW}正在启动服务: $APP_NAME${NC}"
# 优先使用 PM2 管理服务
if startPM2; then
echo -e "${GREEN}服务已通过 PM2 启动并托管,支持崩溃自动重启${NC}"
echo "日志: $LOG_DIR/${APP_NAME}.out.log 和 $LOG_DIR/${APP_NAME}.err.log"
return 0
fi
# 检查服务是否已经运行
if [ -f "$PID_FILE" ]; then
PID=$(cat "$PID_FILE")
if ps -p $PID > /dev/null 2>&1; then
echo -e "${YELLOW}服务已在运行中 (PID: $PID)${NC}"
return 0
else
echo "PID 文件存在但进程不存在,清理后重新启动"
rm -f "$PID_FILE"
fi
fi
# 检查端口是否被占用
if command -v lsof &> /dev/null; then
PORT_CHECK=$(lsof -ti:$APP_PORT 2>/dev/null)
if [ -n "$PORT_CHECK" ]; then
echo -e "${RED}错误: 端口 $APP_PORT 已被占用 (PID: $PORT_CHECK)${NC}"
echo "请先停止占用端口的进程或使用 restart 命令"
return 1
fi
fi
# 检查 .env 文件是否存在
if [ ! -f ".env" ]; then
echo -e "${YELLOW}警告: .env 文件不存在,将使用默认配置${NC}"
echo "建议从 env.example 复制并配置 .env 文件"
fi
# 检查 node_modules 是否存在
if [ ! -d "node_modules" ]; then
echo -e "${RED}错误: node_modules 目录不存在${NC}"
echo "请先运行: npm install"
return 1
fi
# 启动 Node.js 应用
echo "启动命令: NODE_ENV=$NODE_ENV nohup node $ENTRY_FILE > $LOG_FILE 2>&1 &"
NODE_ENV=$NODE_ENV nohup node $ENTRY_FILE > $LOG_FILE 2>&1 &
# 获取新进程的 PID
PID=$!
echo $PID > "$PID_FILE"
# 等待应用启动
echo "等待服务启动..."
sleep 3
# 验证启动是否成功
if ps -p $PID > /dev/null 2>&1; then
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}服务启动成功!${NC}"
echo -e "${GREEN}========================================${NC}"
echo "应用名称: $APP_NAME"
echo "进程 ID: $PID"
echo "监听端口: $APP_PORT"
echo "日志文件: $LOG_FILE"
echo "PID 文件: $PID_FILE"
echo "环境变量: NODE_ENV=$NODE_ENV"
echo -e "${GREEN}API 文档: http://localhost:$APP_PORT/api-docs${NC}"
echo ""
# 等待端口监听
echo "检查端口监听状态..."
sleep 2
if command -v lsof &> /dev/null; then
if lsof -ti:$APP_PORT > /dev/null 2>&1; then
echo -e "${GREEN}✓ 端口 $APP_PORT 正在监听${NC}"
else
echo -e "${YELLOW}⚠ 端口 $APP_PORT 尚未监听,请检查日志${NC}"
fi
fi
# 显示最近的日志
echo ""
echo "最近的启动日志:"
echo "------------------------"
tail -20 "$LOG_FILE"
else
echo -e "${RED}========================================${NC}"
echo -e "${RED}服务启动失败!${NC}"
echo -e "${RED}========================================${NC}"
echo "请检查日志文件: $LOG_FILE"
echo ""
echo "最近的错误日志:"
echo "------------------------"
tail -30 "$LOG_FILE"
rm -f "$PID_FILE"
exit 1
fi
}
# 重启服务函数
function restartApp() {
echo -e "${YELLOW}正在重启服务: $APP_NAME${NC}"
stopApp
sleep 3
startApp
}
# 状态检查函数
function statusApp() {
echo -e "${YELLOW}检查服务状态: $APP_NAME${NC}"
echo "========================================"
# 如果使用 PM2 托管,优先展示 PM2 状态
if command -v pm2 &> /dev/null; then
if pm2 describe "$APP_NAME" > /dev/null 2>&1; then
PM2_STATE=$(pm2 info "$APP_NAME" | grep -i "status" | awk -F":" '{print $2}' | xargs)
echo -e "PM2 状态: ${GREEN}${PM2_STATE}${NC}"
pm2 status "$APP_NAME"
echo ""
fi
fi
if [ -f "$PID_FILE" ]; then
PID=$(cat "$PID_FILE")
if ps -p $PID > /dev/null 2>&1; then
echo -e "${GREEN}✓ 服务正在运行${NC}"
echo "----------------------------------------"
echo "进程 ID: $PID"
echo "应用名称: $APP_NAME"
echo "监听端口: $APP_PORT"
# 启动时间
if command -v ps &> /dev/null; then
START_TIME=$(ps -o lstart= -p $PID 2>/dev/null)
if [ -n "$START_TIME" ]; then
echo "启动时间: $START_TIME"
fi
# 内存使用
MEM_USAGE=$(ps -o rss= -p $PID 2>/dev/null | awk '{printf "%.2f MB", $1/1024}')
if [ -n "$MEM_USAGE" ]; then
echo "内存使用: $MEM_USAGE"
fi
# CPU使用率
CPU_USAGE=$(ps -o %cpu= -p $PID 2>/dev/null)
if [ -n "$CPU_USAGE" ]; then
echo "CPU 使用: ${CPU_USAGE}%"
fi
fi
# 检查端口监听状态
if command -v lsof &> /dev/null; then
PORT_INFO=$(lsof -ti:$APP_PORT 2>/dev/null)
if [ -n "$PORT_INFO" ]; then
echo -e "端口状态: ${GREEN}监听中 ($APP_PORT)${NC}"
else
echo -e "端口状态: ${RED}未监听${NC}"
fi
fi
# 日志文件信息
if [ -f "$LOG_FILE" ]; then
LOG_SIZE=$(du -h "$LOG_FILE" | cut -f1)
echo "日志大小: $LOG_SIZE"
echo "日志文件: $LOG_FILE"
fi
echo ""
echo "最近的日志 (最后10行):"
echo "----------------------------------------"
tail -10 "$LOG_FILE" 2>/dev/null || echo "无法读取日志文件"
else
echo -e "${RED}✗ 服务未运行 (PID 文件存在但进程不存在)${NC}"
echo "建议清理 PID 文件: rm -f $PID_FILE"
fi
else
# 通过端口检查
if command -v lsof &> /dev/null; then
PID=$(lsof -ti:$APP_PORT 2>/dev/null)
if [ -n "$PID" ]; then
echo -e "${YELLOW}⚠ 服务正在运行但未通过脚本管理${NC}"
echo "进程 ID: $PID"
echo "监听端口: $APP_PORT"
echo "建议使用: ./node_manager.sh stop 停止服务后重新启动"
else
echo -e "${RED}✗ 服务未运行${NC}"
echo "使用以下命令启动: ./node_manager.sh start"
fi
else
echo -e "${RED}✗ 服务未运行 (PID 文件不存在)${NC}"
echo "使用以下命令启动: ./node_manager.sh start"
fi
fi
echo "========================================"
}
# 查看日志函数
function viewLogs() {
if [ -f "$LOG_FILE" ]; then
echo -e "${YELLOW}实时查看日志 (Ctrl+C 退出):${NC}"
tail -f "$LOG_FILE"
else
echo -e "${RED}日志文件不存在: $LOG_FILE${NC}"
fi
}
# 主逻辑
case "$1" in
start)
startApp
;;
stop)
stopApp
;;
restart)
restartApp
;;
status)
statusApp
;;
logs)
viewLogs
;;
*)
echo "========================================"
echo "银行端后端bank-backend - 服务管理脚本"
echo "========================================"
echo "使用方法: $0 {start|stop|restart|status|logs}"
echo ""
echo "命令说明:"
echo " start - 启动服务"
echo " stop - 停止服务"
echo " restart - 重启服务"
echo " status - 查看服务状态"
echo " logs - 实时查看日志"
echo ""
echo "增强说明:"
echo " 已优先使用 PM2 管理服务(如安装了 pm2自动重启更可靠"
echo " 配置 PM2 开机自启: pm2 startup systemd -u $(whoami) --hp $HOME; pm2 save"
echo ""
echo "配置信息:"
echo " 应用名称: $APP_NAME"
echo " 入口文件: $ENTRY_FILE"
echo " 监听端口: $APP_PORT"
echo " 日志文件: $LOG_FILE"
echo ""
echo "示例:"
echo " $0 start # 启动服务"
echo " $0 status # 查看状态"
echo " $0 logs # 查看日志"
exit 1
;;
esac
exit 0