control_scripts.sh 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. #!/bin/bash
  2. # ===========================================
  3. # 学生状态变更处理脚本控制程序 - 生产环境版本
  4. # 用于启动、停止、重启处理脚本 (Ubuntu兼容性修复版本)
  5. # ===========================================
  6. #
  7. # 生产环境部署指南:
  8. # 1. 确保所有修复版本脚本存在:
  9. # - process_student_changes_fixed.sh (状态变更处理)
  10. # - process_student_resume_fixed.sh (复学处理)
  11. #
  12. # 2. 确保配置文件存在:
  13. # - data.conf (数据库配置和脚本参数)
  14. #
  15. # 3. 启动所有服务:
  16. # ./control_scripts.sh startall
  17. #
  18. # 4. 监控运行状态:
  19. # ./control_scripts.sh statusall
  20. #
  21. # 5. 健康检查:
  22. # ./control_scripts.sh health all
  23. #
  24. # 6. 查看系统资源:
  25. # ./control_scripts.sh info
  26. #
  27. # 日志文件位置:
  28. # - 控制脚本日志: logs/control_scripts_YYYYMMDD.log
  29. # - 处理脚本日志: student_status_change/process_student_*_YYYYMMDD_HHMMSS.log
  30. # Disable strict error checking for better production resilience
  31. # set -euo pipefail # Temporarily disabled for production robustness
  32. # Force C locale for consistent behavior across systems
  33. export LANG=C
  34. export LC_ALL=C
  35. # Load configuration
  36. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  37. CONFIG_FILE="${SCRIPT_DIR}/data.conf"
  38. if [[ ! -f "$CONFIG_FILE" ]]; then
  39. echo "错误:配置文件 $CONFIG_FILE 未找到!"
  40. echo "请确保 data.conf 文件存在于脚本目录中"
  41. exit 1
  42. fi
  43. source "$CONFIG_FILE"
  44. # Production environment setup
  45. PRODUCTION_LOG_DIR="${SCRIPT_DIR}/logs"
  46. mkdir -p "$PRODUCTION_LOG_DIR"
  47. PRODUCTION_LOG_FILE="${PRODUCTION_LOG_DIR}/control_scripts_$(date +%Y%m%d).log"
  48. # Script definitions - Production versions with Ubuntu compatibility fixes
  49. # Using indexed arrays for better bash compatibility
  50. SCRIPT_NAMES=("changes" "resume")
  51. SCRIPT_FILES=("process_student_changes_fixed.sh" "process_student_resume_fixed.sh")
  52. # Function to get script file by name
  53. get_script_file() {
  54. local script_name="$1"
  55. for i in "${!SCRIPT_NAMES[@]}"; do
  56. if [[ "${SCRIPT_NAMES[$i]}" == "$script_name" ]]; then
  57. echo "${SCRIPT_FILES[$i]}"
  58. return 0
  59. fi
  60. done
  61. return 1
  62. }
  63. # PID files
  64. PID_DIR="${SCRIPT_DIR}/pids"
  65. mkdir -p "$PID_DIR"
  66. # Functions
  67. log() {
  68. local level="$1"
  69. local message="$2"
  70. local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
  71. local log_message="[$timestamp] [$level] $message"
  72. # Output to console
  73. echo "$log_message"
  74. # Output to production log file
  75. echo "$log_message" >> "$PRODUCTION_LOG_FILE"
  76. }
  77. get_pid_file() {
  78. local script_name="$1"
  79. echo "${PID_DIR}/${script_name}.pid"
  80. }
  81. get_lock_file() {
  82. local script_name="$1"
  83. echo "${SCRIPT_DIR}/${script_name}.lock"
  84. }
  85. is_running() {
  86. local script_name="$1"
  87. local pid_file=$(get_pid_file "$script_name")
  88. local lock_file=$(get_lock_file "$script_name")
  89. # Check PID file
  90. if [[ -f "$pid_file" ]]; then
  91. local pid=$(cat "$pid_file")
  92. if kill -0 "$pid" 2>/dev/null; then
  93. return 0 # Running
  94. else
  95. log "WARN" "删除脚本 $script_name 的过期PID文件"
  96. rm -f "$pid_file"
  97. fi
  98. fi
  99. # Check lock file
  100. if [[ -f "$lock_file" ]]; then
  101. local lock_pid=$(cat "$lock_file")
  102. if kill -0 "$lock_pid" 2>/dev/null; then
  103. return 0 # Running
  104. else
  105. log "WARN" "删除脚本 $script_name 的过期锁定文件"
  106. rm -f "$lock_file"
  107. fi
  108. fi
  109. return 1 # Not running
  110. }
  111. # Health check function for production monitoring
  112. health_check() {
  113. local script_name="$1"
  114. local script_file="${SCRIPT_DIR}/$(get_script_file "$script_name")"
  115. log "INFO" "执行 $script_name 的健康检查..."
  116. # Check if script file exists and is executable
  117. if [[ ! -f "$script_file" ]]; then
  118. log "ERROR" "$script_name 脚本文件不存在: $script_file"
  119. return 1
  120. fi
  121. if [[ ! -x "$script_file" ]]; then
  122. log "WARN" "$script_name 脚本文件没有执行权限,正在修复..."
  123. chmod +x "$script_file"
  124. fi
  125. # Check database connectivity
  126. if ! mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1;" >/dev/null 2>&1; then
  127. log "ERROR" "$script_name 数据库连接失败"
  128. return 1
  129. fi
  130. # Check if script is running
  131. if ! is_running "$script_name"; then
  132. log "WARN" "$script_name 脚本未运行"
  133. return 1
  134. fi
  135. log "INFO" "$script_name 健康检查通过"
  136. return 0
  137. }
  138. # Get system resource usage
  139. get_system_info() {
  140. log "INFO" "=== 系统资源信息 ==="
  141. # CPU usage
  142. local cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
  143. log "INFO" "CPU使用率: ${cpu_usage}%"
  144. # Memory usage
  145. local mem_info=$(free -h | grep "^Mem:")
  146. log "INFO" "内存信息: $mem_info"
  147. # Disk usage
  148. local disk_info=$(df -h "$SCRIPT_DIR" | tail -1 | awk '{print $1 ": " $5 " used (" $4 " free)"}')
  149. log "INFO" "磁盘使用: $disk_info"
  150. # Process count
  151. local process_count=$(pgrep -f "process_student.*\.sh" | wc -l)
  152. log "INFO" "运行中的学生处理进程数: $process_count"
  153. }
  154. start_script() {
  155. local script_name="$1"
  156. local script_file="${SCRIPT_DIR}/$(get_script_file "$script_name")"
  157. local pid_file=$(get_pid_file "$script_name")
  158. if [[ ! -f "$script_file" ]]; then
  159. log "ERROR" "脚本文件未找到: $script_file"
  160. return 1
  161. fi
  162. if is_running "$script_name"; then
  163. log "INFO" "$script_name 已在运行"
  164. return 0
  165. fi
  166. log "INFO" "正在启动 $script_name..."
  167. chmod +x "$script_file"
  168. # Start script in background
  169. nohup "$script_file" > /dev/null 2>&1 &
  170. local pid=$!
  171. # Wait a moment for script to start
  172. sleep 2
  173. if kill -0 "$pid" 2>/dev/null; then
  174. echo $pid > "$pid_file"
  175. log "INFO" "$script_name 启动成功 (PID: $pid)"
  176. return 0
  177. else
  178. log "ERROR" "$script_name 启动失败"
  179. return 1
  180. fi
  181. }
  182. stop_script() {
  183. local script_name="$1"
  184. local pid_file=$(get_pid_file "$script_name")
  185. local lock_file=$(get_lock_file "$script_name")
  186. local stopped=false
  187. log "INFO" "正在停止 $script_name..."
  188. # 尝试通过PID文件停止
  189. if [[ -f "$pid_file" ]]; then
  190. local pid=$(cat "$pid_file")
  191. if kill -0 "$pid" 2>/dev/null; then
  192. kill "$pid"
  193. sleep 2
  194. if ! kill -0 "$pid" 2>/dev/null; then
  195. log "INFO" "$script_name 停止成功 (PID: $pid)"
  196. stopped=true
  197. fi
  198. fi
  199. rm -f "$pid_file"
  200. fi
  201. # 尝试通过锁定文件停止
  202. if [[ -f "$lock_file" ]]; then
  203. local lock_pid=$(cat "$lock_file")
  204. if kill -0 "$lock_pid" 2>/dev/null; then
  205. kill "$lock_pid"
  206. sleep 2
  207. if ! kill -0 "$lock_pid" 2>/dev/null; then
  208. log "INFO" "$script_name 停止成功 (PID: $lock_pid)"
  209. stopped=true
  210. fi
  211. fi
  212. rm -f "$lock_file"
  213. fi
  214. if [[ "$stopped" == false ]]; then
  215. log "WARN" "$script_name 可能未运行或优雅停止失败"
  216. fi
  217. }
  218. restart_script() {
  219. local script_name="$1"
  220. log "INFO" "正在重启 $script_name..."
  221. stop_script "$script_name"
  222. sleep 3
  223. start_script "$script_name"
  224. }
  225. status_script() {
  226. local script_name="$1"
  227. if is_running "$script_name"; then
  228. local pid_file=$(get_pid_file "$script_name")
  229. local lock_file=$(get_lock_file "$script_name")
  230. if [[ -f "$pid_file" ]]; then
  231. local pid=$(cat "$pid_file")
  232. log "INFO" "$script_name 正在运行 (PID: $pid)"
  233. elif [[ -f "$lock_file" ]]; then
  234. local lock_pid=$(cat "$lock_file")
  235. log "INFO" "$script_name 正在运行 (PID: $lock_pid)"
  236. else
  237. log "INFO" "$script_name 状态未知"
  238. fi
  239. else
  240. log "INFO" "$script_name 未运行"
  241. fi
  242. }
  243. show_usage() {
  244. cat << EOF
  245. 用法: $0 <命令> <脚本名称>
  246. 命令:
  247. start 启动指定的脚本
  248. stop 停止指定的脚本
  249. restart 重启指定的脚本
  250. status 显示指定脚本的状态
  251. health 执行脚本健康检查
  252. startall 启动所有脚本
  253. stopall 停止所有脚本
  254. statusall 显示所有脚本的状态
  255. info 显示系统资源信息
  256. 脚本名称:
  257. changes 学生状态变更脚本 (休学/退学/毕业) - Ubuntu兼容修复版本
  258. resume 学生复学脚本 (复学) - Ubuntu兼容修复版本
  259. all 所有脚本
  260. 生产环境示例:
  261. $0 startall # 启动所有学生处理脚本
  262. $0 statusall # 检查所有脚本运行状态
  263. $0 health all # 执行所有脚本的健康检查
  264. $0 info # 查看系统资源使用情况
  265. $0 restart changes # 重启状态变更处理脚本
  266. $0 stopall # 停止所有处理脚本
  267. 传统示例:
  268. $0 start changes # 启动状态变更处理脚本
  269. $0 stop resume # 停止复学处理脚本
  270. $0 status changes # 显示状态变更脚本状态
  271. 注意:
  272. 本脚本使用修复版本,已适配Ubuntu环境,解决了循环处理和数据库查询的兼容性问题
  273. EOF
  274. }
  275. # Production environment initialization
  276. init_production_env() {
  277. log "INFO" "初始化生产环境..."
  278. # Check script files exist
  279. for script_name in "${!SCRIPTS[@]}"; do
  280. local script_file="${SCRIPT_DIR}/${SCRIPTS[$script_name]}"
  281. if [[ ! -f "$script_file" ]]; then
  282. log "ERROR" "脚本文件不存在: $script_file"
  283. log "ERROR" "请确保已部署修复版本的脚本文件"
  284. exit 1
  285. fi
  286. done
  287. # Test database connectivity
  288. if ! mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1;" >/dev/null 2>&1; then
  289. log "ERROR" "数据库连接失败,请检查配置"
  290. exit 1
  291. fi
  292. log "INFO" "生产环境初始化完成"
  293. }
  294. # Main function
  295. main() {
  296. # Initialize production environment
  297. init_production_env
  298. local command="${1:-}"
  299. local script_name="${2:-}"
  300. case "$command" in
  301. start)
  302. if [[ "$script_name" == "all" ]]; then
  303. for script in "${SCRIPT_NAMES[@]}"; do
  304. start_script "$script"
  305. done
  306. elif get_script_file "$script_name" >/dev/null 2>&1; then
  307. start_script "$script_name"
  308. else
  309. log "ERROR" "未知的脚本名称: $script_name"
  310. show_usage
  311. exit 1
  312. fi
  313. ;;
  314. stop)
  315. if [[ "$script_name" == "all" ]]; then
  316. for script in "${SCRIPT_NAMES[@]}"; do
  317. stop_script "$script"
  318. done
  319. elif get_script_file "$script_name" >/dev/null 2>&1; then
  320. stop_script "$script_name"
  321. else
  322. log "ERROR" "未知的脚本名称: $script_name"
  323. show_usage
  324. exit 1
  325. fi
  326. ;;
  327. restart)
  328. if [[ "$script_name" == "all" ]]; then
  329. for script in "${SCRIPT_NAMES[@]}"; do
  330. restart_script "$script"
  331. done
  332. elif get_script_file "$script_name" >/dev/null 2>&1; then
  333. restart_script "$script_name"
  334. else
  335. log "ERROR" "未知的脚本名称: $script_name"
  336. show_usage
  337. exit 1
  338. fi
  339. ;;
  340. status)
  341. if [[ "$script_name" == "all" ]]; then
  342. for script in "${SCRIPT_NAMES[@]}"; do
  343. status_script "$script"
  344. done
  345. elif get_script_file "$script_name" >/dev/null 2>&1; then
  346. status_script "$script_name"
  347. else
  348. log "ERROR" "未知的脚本名称: $script_name"
  349. show_usage
  350. exit 1
  351. fi
  352. ;;
  353. startall)
  354. log "INFO" "正在启动所有脚本..."
  355. for script in "${SCRIPT_NAMES[@]}"; do
  356. start_script "$script"
  357. done
  358. ;;
  359. stopall)
  360. log "INFO" "正在停止所有脚本..."
  361. for script in "${SCRIPT_NAMES[@]}"; do
  362. stop_script "$script"
  363. done
  364. ;;
  365. statusall)
  366. log "INFO" "正在检查所有脚本的状态..."
  367. for script in "${SCRIPT_NAMES[@]}"; do
  368. status_script "$script"
  369. done
  370. ;;
  371. health)
  372. if [[ "$script_name" == "all" ]]; then
  373. local all_healthy=true
  374. for script in "${SCRIPT_NAMES[@]}"; do
  375. if ! health_check "$script"; then
  376. all_healthy=false
  377. fi
  378. done
  379. if [[ "$all_healthy" == true ]]; then
  380. log "INFO" "所有脚本健康检查通过"
  381. exit 0
  382. else
  383. log "ERROR" "部分脚本健康检查失败"
  384. exit 1
  385. fi
  386. elif get_script_file "$script_name" >/dev/null 2>&1; then
  387. if health_check "$script_name"; then
  388. log "INFO" "$script_name 健康检查通过"
  389. exit 0
  390. else
  391. log "ERROR" "$script_name 健康检查失败"
  392. exit 1
  393. fi
  394. else
  395. log "ERROR" "未知的脚本名称: $script_name"
  396. show_usage
  397. exit 1
  398. fi
  399. ;;
  400. info)
  401. get_system_info
  402. ;;
  403. *)
  404. log "ERROR" "未知命令: $command"
  405. show_usage
  406. exit 1
  407. ;;
  408. esac
  409. }
  410. # Run main function
  411. main "$@"