482 lines
14 KiB
Bash
Executable File
482 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ===========================================
|
|
# 学生状态变更处理脚本控制程序 - 生产环境版本
|
|
# 用于启动、停止、重启处理脚本 (Ubuntu兼容性修复版本)
|
|
# ===========================================
|
|
#
|
|
# 生产环境部署指南:
|
|
# 1. 确保所有修复版本脚本存在:
|
|
# - process_student_changes_fixed.sh (状态变更处理)
|
|
# - process_student_resume_fixed.sh (复学处理)
|
|
#
|
|
# 2. 确保配置文件存在:
|
|
# - data.conf (数据库配置和脚本参数)
|
|
#
|
|
# 3. 启动所有服务:
|
|
# ./control_scripts.sh startall
|
|
#
|
|
# 4. 监控运行状态:
|
|
# ./control_scripts.sh statusall
|
|
#
|
|
# 5. 健康检查:
|
|
# ./control_scripts.sh health all
|
|
#
|
|
# 6. 查看系统资源:
|
|
# ./control_scripts.sh info
|
|
#
|
|
# 日志文件位置:
|
|
# - 控制脚本日志: logs/control_scripts_YYYYMMDD.log
|
|
# - 处理脚本日志: student_status_change/process_student_*_YYYYMMDD_HHMMSS.log
|
|
|
|
# Disable strict error checking for better production resilience
|
|
# set -euo pipefail # Temporarily disabled for production robustness
|
|
|
|
# Force C locale for consistent behavior across systems
|
|
export LANG=C
|
|
export LC_ALL=C
|
|
|
|
# Load configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
CONFIG_FILE="${SCRIPT_DIR}/data.conf"
|
|
|
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
echo "错误:配置文件 $CONFIG_FILE 未找到!"
|
|
echo "请确保 data.conf 文件存在于脚本目录中"
|
|
exit 1
|
|
fi
|
|
|
|
source "$CONFIG_FILE"
|
|
|
|
# Production environment setup
|
|
PRODUCTION_LOG_DIR="${SCRIPT_DIR}/logs"
|
|
mkdir -p "$PRODUCTION_LOG_DIR"
|
|
PRODUCTION_LOG_FILE="${PRODUCTION_LOG_DIR}/control_scripts_$(date +%Y%m%d).log"
|
|
|
|
# Script definitions - Production versions with Ubuntu compatibility fixes
|
|
# Using indexed arrays for better bash compatibility
|
|
SCRIPT_NAMES=("changes" "resume")
|
|
SCRIPT_FILES=("process_student_changes_fixed.sh" "process_student_resume_fixed.sh")
|
|
|
|
# Function to get script file by name
|
|
get_script_file() {
|
|
local script_name="$1"
|
|
for i in "${!SCRIPT_NAMES[@]}"; do
|
|
if [[ "${SCRIPT_NAMES[$i]}" == "$script_name" ]]; then
|
|
echo "${SCRIPT_FILES[$i]}"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# PID files
|
|
PID_DIR="${SCRIPT_DIR}/pids"
|
|
mkdir -p "$PID_DIR"
|
|
|
|
# Functions
|
|
log() {
|
|
local level="$1"
|
|
local message="$2"
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
local log_message="[$timestamp] [$level] $message"
|
|
|
|
# Output to console
|
|
echo "$log_message"
|
|
|
|
# Output to production log file
|
|
echo "$log_message" >> "$PRODUCTION_LOG_FILE"
|
|
}
|
|
|
|
get_pid_file() {
|
|
local script_name="$1"
|
|
echo "${PID_DIR}/${script_name}.pid"
|
|
}
|
|
|
|
get_lock_file() {
|
|
local script_name="$1"
|
|
echo "${SCRIPT_DIR}/${script_name}.lock"
|
|
}
|
|
|
|
is_running() {
|
|
local script_name="$1"
|
|
local pid_file=$(get_pid_file "$script_name")
|
|
local lock_file=$(get_lock_file "$script_name")
|
|
|
|
# Check PID file
|
|
if [[ -f "$pid_file" ]]; then
|
|
local pid=$(cat "$pid_file")
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
return 0 # Running
|
|
else
|
|
log "WARN" "删除脚本 $script_name 的过期PID文件"
|
|
rm -f "$pid_file"
|
|
fi
|
|
fi
|
|
|
|
# Check lock file
|
|
if [[ -f "$lock_file" ]]; then
|
|
local lock_pid=$(cat "$lock_file")
|
|
if kill -0 "$lock_pid" 2>/dev/null; then
|
|
return 0 # Running
|
|
else
|
|
log "WARN" "删除脚本 $script_name 的过期锁定文件"
|
|
rm -f "$lock_file"
|
|
fi
|
|
fi
|
|
|
|
return 1 # Not running
|
|
}
|
|
|
|
# Health check function for production monitoring
|
|
health_check() {
|
|
local script_name="$1"
|
|
local script_file="${SCRIPT_DIR}/$(get_script_file "$script_name")"
|
|
|
|
log "INFO" "执行 $script_name 的健康检查..."
|
|
|
|
# Check if script file exists and is executable
|
|
if [[ ! -f "$script_file" ]]; then
|
|
log "ERROR" "$script_name 脚本文件不存在: $script_file"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -x "$script_file" ]]; then
|
|
log "WARN" "$script_name 脚本文件没有执行权限,正在修复..."
|
|
chmod +x "$script_file"
|
|
fi
|
|
|
|
# Check database connectivity
|
|
if ! mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1;" >/dev/null 2>&1; then
|
|
log "ERROR" "$script_name 数据库连接失败"
|
|
return 1
|
|
fi
|
|
|
|
# Check if script is running
|
|
if ! is_running "$script_name"; then
|
|
log "WARN" "$script_name 脚本未运行"
|
|
return 1
|
|
fi
|
|
|
|
log "INFO" "$script_name 健康检查通过"
|
|
return 0
|
|
}
|
|
|
|
# Get system resource usage
|
|
get_system_info() {
|
|
log "INFO" "=== 系统资源信息 ==="
|
|
|
|
# CPU usage
|
|
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
|
|
log "INFO" "CPU使用率: ${cpu_usage}%"
|
|
|
|
# Memory usage
|
|
local mem_info=$(free -h | grep "^Mem:")
|
|
log "INFO" "内存信息: $mem_info"
|
|
|
|
# Disk usage
|
|
local disk_info=$(df -h "$SCRIPT_DIR" | tail -1 | awk '{print $1 ": " $5 " used (" $4 " free)"}')
|
|
log "INFO" "磁盘使用: $disk_info"
|
|
|
|
# Process count
|
|
local process_count=$(pgrep -f "process_student.*\.sh" | wc -l)
|
|
log "INFO" "运行中的学生处理进程数: $process_count"
|
|
}
|
|
|
|
start_script() {
|
|
local script_name="$1"
|
|
local script_file="${SCRIPT_DIR}/$(get_script_file "$script_name")"
|
|
local pid_file=$(get_pid_file "$script_name")
|
|
|
|
if [[ ! -f "$script_file" ]]; then
|
|
log "ERROR" "脚本文件未找到: $script_file"
|
|
return 1
|
|
fi
|
|
|
|
if is_running "$script_name"; then
|
|
log "INFO" "$script_name 已在运行"
|
|
return 0
|
|
fi
|
|
|
|
log "INFO" "正在启动 $script_name..."
|
|
chmod +x "$script_file"
|
|
|
|
# Start script in background
|
|
nohup "$script_file" > /dev/null 2>&1 &
|
|
local pid=$!
|
|
|
|
# Wait a moment for script to start
|
|
sleep 2
|
|
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
echo $pid > "$pid_file"
|
|
log "INFO" "$script_name 启动成功 (PID: $pid)"
|
|
return 0
|
|
else
|
|
log "ERROR" "$script_name 启动失败"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
stop_script() {
|
|
local script_name="$1"
|
|
local pid_file=$(get_pid_file "$script_name")
|
|
local lock_file=$(get_lock_file "$script_name")
|
|
local stopped=false
|
|
|
|
log "INFO" "正在停止 $script_name..."
|
|
|
|
# 尝试通过PID文件停止
|
|
if [[ -f "$pid_file" ]]; then
|
|
local pid=$(cat "$pid_file")
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
kill "$pid"
|
|
sleep 2
|
|
if ! kill -0 "$pid" 2>/dev/null; then
|
|
log "INFO" "$script_name 停止成功 (PID: $pid)"
|
|
stopped=true
|
|
fi
|
|
fi
|
|
rm -f "$pid_file"
|
|
fi
|
|
|
|
# 尝试通过锁定文件停止
|
|
if [[ -f "$lock_file" ]]; then
|
|
local lock_pid=$(cat "$lock_file")
|
|
if kill -0 "$lock_pid" 2>/dev/null; then
|
|
kill "$lock_pid"
|
|
sleep 2
|
|
if ! kill -0 "$lock_pid" 2>/dev/null; then
|
|
log "INFO" "$script_name 停止成功 (PID: $lock_pid)"
|
|
stopped=true
|
|
fi
|
|
fi
|
|
rm -f "$lock_file"
|
|
fi
|
|
|
|
if [[ "$stopped" == false ]]; then
|
|
log "WARN" "$script_name 可能未运行或优雅停止失败"
|
|
fi
|
|
}
|
|
|
|
restart_script() {
|
|
local script_name="$1"
|
|
log "INFO" "正在重启 $script_name..."
|
|
stop_script "$script_name"
|
|
sleep 3
|
|
start_script "$script_name"
|
|
}
|
|
|
|
status_script() {
|
|
local script_name="$1"
|
|
if is_running "$script_name"; then
|
|
local pid_file=$(get_pid_file "$script_name")
|
|
local lock_file=$(get_lock_file "$script_name")
|
|
|
|
if [[ -f "$pid_file" ]]; then
|
|
local pid=$(cat "$pid_file")
|
|
log "INFO" "$script_name 正在运行 (PID: $pid)"
|
|
elif [[ -f "$lock_file" ]]; then
|
|
local lock_pid=$(cat "$lock_file")
|
|
log "INFO" "$script_name 正在运行 (PID: $lock_pid)"
|
|
else
|
|
log "INFO" "$script_name 状态未知"
|
|
fi
|
|
else
|
|
log "INFO" "$script_name 未运行"
|
|
fi
|
|
}
|
|
|
|
show_usage() {
|
|
cat << EOF
|
|
用法: $0 <命令> <脚本名称>
|
|
|
|
命令:
|
|
start 启动指定的脚本
|
|
stop 停止指定的脚本
|
|
restart 重启指定的脚本
|
|
status 显示指定脚本的状态
|
|
health 执行脚本健康检查
|
|
startall 启动所有脚本
|
|
stopall 停止所有脚本
|
|
statusall 显示所有脚本的状态
|
|
info 显示系统资源信息
|
|
|
|
脚本名称:
|
|
changes 学生状态变更脚本 (休学/退学/毕业) - Ubuntu兼容修复版本
|
|
resume 学生复学脚本 (复学) - Ubuntu兼容修复版本
|
|
all 所有脚本
|
|
|
|
生产环境示例:
|
|
$0 startall # 启动所有学生处理脚本
|
|
$0 statusall # 检查所有脚本运行状态
|
|
$0 health all # 执行所有脚本的健康检查
|
|
$0 info # 查看系统资源使用情况
|
|
$0 restart changes # 重启状态变更处理脚本
|
|
$0 stopall # 停止所有处理脚本
|
|
|
|
传统示例:
|
|
$0 start changes # 启动状态变更处理脚本
|
|
$0 stop resume # 停止复学处理脚本
|
|
$0 status changes # 显示状态变更脚本状态
|
|
|
|
注意:
|
|
本脚本使用修复版本,已适配Ubuntu环境,解决了循环处理和数据库查询的兼容性问题
|
|
|
|
EOF
|
|
}
|
|
|
|
# Production environment initialization
|
|
init_production_env() {
|
|
log "INFO" "初始化生产环境..."
|
|
|
|
# Check script files exist
|
|
for script_name in "${!SCRIPTS[@]}"; do
|
|
local script_file="${SCRIPT_DIR}/${SCRIPTS[$script_name]}"
|
|
if [[ ! -f "$script_file" ]]; then
|
|
log "ERROR" "脚本文件不存在: $script_file"
|
|
log "ERROR" "请确保已部署修复版本的脚本文件"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# Test database connectivity
|
|
if ! mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1;" >/dev/null 2>&1; then
|
|
log "ERROR" "数据库连接失败,请检查配置"
|
|
exit 1
|
|
fi
|
|
|
|
log "INFO" "生产环境初始化完成"
|
|
}
|
|
|
|
# Main function
|
|
main() {
|
|
# Initialize production environment
|
|
init_production_env
|
|
|
|
local command="${1:-}"
|
|
local script_name="${2:-}"
|
|
|
|
case "$command" in
|
|
start)
|
|
if [[ "$script_name" == "all" ]]; then
|
|
for script in "${SCRIPT_NAMES[@]}"; do
|
|
start_script "$script"
|
|
done
|
|
elif get_script_file "$script_name" >/dev/null 2>&1; then
|
|
start_script "$script_name"
|
|
else
|
|
log "ERROR" "未知的脚本名称: $script_name"
|
|
show_usage
|
|
exit 1
|
|
fi
|
|
;;
|
|
|
|
stop)
|
|
if [[ "$script_name" == "all" ]]; then
|
|
for script in "${SCRIPT_NAMES[@]}"; do
|
|
stop_script "$script"
|
|
done
|
|
elif get_script_file "$script_name" >/dev/null 2>&1; then
|
|
stop_script "$script_name"
|
|
else
|
|
log "ERROR" "未知的脚本名称: $script_name"
|
|
show_usage
|
|
exit 1
|
|
fi
|
|
;;
|
|
|
|
restart)
|
|
if [[ "$script_name" == "all" ]]; then
|
|
for script in "${SCRIPT_NAMES[@]}"; do
|
|
restart_script "$script"
|
|
done
|
|
elif get_script_file "$script_name" >/dev/null 2>&1; then
|
|
restart_script "$script_name"
|
|
else
|
|
log "ERROR" "未知的脚本名称: $script_name"
|
|
show_usage
|
|
exit 1
|
|
fi
|
|
;;
|
|
|
|
status)
|
|
if [[ "$script_name" == "all" ]]; then
|
|
for script in "${SCRIPT_NAMES[@]}"; do
|
|
status_script "$script"
|
|
done
|
|
elif get_script_file "$script_name" >/dev/null 2>&1; then
|
|
status_script "$script_name"
|
|
else
|
|
log "ERROR" "未知的脚本名称: $script_name"
|
|
show_usage
|
|
exit 1
|
|
fi
|
|
;;
|
|
|
|
startall)
|
|
log "INFO" "正在启动所有脚本..."
|
|
for script in "${SCRIPT_NAMES[@]}"; do
|
|
start_script "$script"
|
|
done
|
|
;;
|
|
|
|
stopall)
|
|
log "INFO" "正在停止所有脚本..."
|
|
for script in "${SCRIPT_NAMES[@]}"; do
|
|
stop_script "$script"
|
|
done
|
|
;;
|
|
|
|
statusall)
|
|
log "INFO" "正在检查所有脚本的状态..."
|
|
for script in "${SCRIPT_NAMES[@]}"; do
|
|
status_script "$script"
|
|
done
|
|
;;
|
|
|
|
health)
|
|
if [[ "$script_name" == "all" ]]; then
|
|
local all_healthy=true
|
|
for script in "${SCRIPT_NAMES[@]}"; do
|
|
if ! health_check "$script"; then
|
|
all_healthy=false
|
|
fi
|
|
done
|
|
if [[ "$all_healthy" == true ]]; then
|
|
log "INFO" "所有脚本健康检查通过"
|
|
exit 0
|
|
else
|
|
log "ERROR" "部分脚本健康检查失败"
|
|
exit 1
|
|
fi
|
|
elif get_script_file "$script_name" >/dev/null 2>&1; then
|
|
if health_check "$script_name"; then
|
|
log "INFO" "$script_name 健康检查通过"
|
|
exit 0
|
|
else
|
|
log "ERROR" "$script_name 健康检查失败"
|
|
exit 1
|
|
fi
|
|
else
|
|
log "ERROR" "未知的脚本名称: $script_name"
|
|
show_usage
|
|
exit 1
|
|
fi
|
|
;;
|
|
|
|
info)
|
|
get_system_info
|
|
;;
|
|
|
|
*)
|
|
log "ERROR" "未知命令: $command"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|