提交 eaa16b43 authored 作者: 陈泽健's avatar 陈泽健

Merge remote-tracking branch 'origin/develop' into develop

......@@ -77,7 +77,7 @@ start_service() {
nohup "./$(basename "$script_path")" > startup.log 2>&1 &
# 等待一小段时间,让进程有机会启动
sleep 3
sleep 600
# 再次检查进程是否启动成功
if is_process_running "$service_name"; then
......
#!/bin/bash
echo "==============================================="
echo " Linux 已删除但仍被占用文件排查工具 (内核直读版)"
echo "==============================================="
echo -e "\n🔍 正在扫描被删除但仍被进程占用的文件...\n"
total_size=0
found_count=0
# 遍历所有进程的 fd 目录
for fd_path in /proc/[0-9]*/fd/*; do
# 检查是否为符号链接且指向已删除文件
if [ -L "$fd_path" ] && ls -l "$fd_path" 2>/dev/null | grep -q "(deleted)"; then
# 提取 PID 和 FD
pid=$(echo "$fd_path" | cut -d'/' -f3)
fd=$(echo "$fd_path" | cut -d'/' -f5)
# 获取原始文件路径
target_file=$(readlink "$fd_path")
# 获取进程名
if [ -f "/proc/$pid/comm" ]; then
proc_name=$(cat "/proc/$pid/comm")
else
proc_name="unknown"
fi
# 获取文件大小 (通过 stat 命令获取该 fd 对应的大小)
# 注意:对于已删除文件,直接 stat fd 路径是获取该文件大小最准确的方法
size_bytes=$(stat -L -c %s "$fd_path" 2>/dev/null || echo 0)
if [ "$size_bytes" -gt 0 ] || [[ "$target_file" == *"$TARGET_KEY"* ]]; then
found_count=$((found_count + 1))
total_size=$((total_size + size_bytes))
size_mb=$(echo "scale=2; $size_bytes / 1024 / 1024" | bc)
echo "--------------------------------------------------"
printf "进程: %-15s | PID: %-8s | FD: %-5s\n" "$proc_name" "$pid" "$fd"
printf "占用: %-10s MB | 文件: %s\n" "$size_mb" "$target_file"
fi
fi
done
if [ "$found_count" -eq 0 ]; then
echo "✅ 未发现被占用的已删除文件,磁盘空间正常。"
else
total_mb=$(echo "scale=2; $total_size / 1024 / 1024" | bc)
total_gb=$(echo "scale=2; $total_size / 1024 / 1024 / 1024" | bc)
echo "=================================================="
printf "总共被删除但未释放的空间:%.2f MB (%.2f GB)\n" "$total_mb" "$total_gb"
echo "=================================================="
fi
echo -e "\n📌 处理建议:"
echo "1) 重启占用文件的进程即可释放空间(推荐)"
echo "2) 或执行: kill -9 <PID>"
echo "3) 若为日志文件,建议检查日志轮转策略(如 logrotate 是否配置了 copytruncate)"
echo -e "\n🎉 扫描完成。\n"
......@@ -3,7 +3,7 @@
# ================= 配置区域 =================
TARGET_KEY="ubains-INFO-AND-ERROR"
MIN_SIZE=$((1024*1024*1024)) # 1GB (单位:字节)
LOG_FILE="$(dirname "$0")/auto_clean_deleted_ubains.log"
LOG_FILE="/var/log/scripts/auto_clean_deleted_ubains.log"
MAX_LOG_SIZE=$((5*1024*1024)) # 5MB 日志大小限制
LOG_RETENTION_DAYS=7 # 日志保留天数
CONTAINER_NAME="ujava2" # 容器名称
......@@ -24,7 +24,8 @@ rotate_logs() {
log "日志文件超过 5MB,已自动轮转。"
fi
fi
find "$(dirname "$0")" -name "auto_clean_deleted_ubains.log.*" -mtime +$LOG_RETENTION_DAYS -exec rm -f {} \;
# 修改为在日志文件所在目录查找备份文件
find "$(dirname "$LOG_FILE")" -name "auto_clean_deleted_ubains.log.*" -mtime +$LOG_RETENTION_DAYS -exec rm -f {} \;
}
rotate_logs
......
......@@ -105,7 +105,7 @@ start_service() {
nohup "./$(basename "$script_path")" > startup.log 2>&1 &
# 等待一小段时间,让进程有机会启动
sleep 3
sleep 600
# 再次检查进程是否启动成功
if is_process_running "$service_name"; then
......
#!/bin/bash
#===============================================================================
# 脚本名称:monitor_external_api_services.sh
# 功能描述:外部 API 服务监测与自愈脚本 (严格精确匹配版)
# 版本:V2.1
# 创建日期:2026-03-05
#
# 核心策略:
# 1. Java Jar 包:精确匹配命令行中包含特定 Jar 文件名的 Java 进程。
# 2. 普通进程:严格使用 pgrep -x 进行全名匹配,拒绝模糊搜索。
# 3. 启动验证:轮询检测,确保进程真正拉起。
#
# 使用方法:
# chmod +x monitor_external_api_services.sh
# */5 * * * * /opt/scripts/monitor_external_api_services.sh >> /var/log/cron_monitor.log 2>&1
#===============================================================================
# --- 配置区域 ---
set -euo pipefail
# 日志配置
LOG_DIR="/var/log/scripts"
LOG_FILE="${LOG_DIR}/monitor_external_api_services.log"
# 监控参数
STARTUP_TIMEOUT=600 # 最大等待启动时间 (秒)
POLL_INTERVAL=3 # 状态轮询间隔 (秒)
STOP_WAIT_TIME=30 # 停止残留进程的等待时间 (秒)
# 定义要监控的服务
# 格式: "进程标识:目录路径:启动脚本路径"
# 注意:
# - 对于 Jar 包,'进程标识' 必须是 Jar 文件的完整名称 (如 app-1.0.jar)
# - 对于普通程序,'进程标识' 必须是准确的进程名 (如 nginx, malan)
SERVICES=(
"ubains-meeting-api-1.0-SNAPSHOT.jar:/var/www/java/external-meeting-api:/var/www/java/external-meeting-api/run.sh"
"malan:/var/www/malan:/var/www/malan/run.sh"
)
# --- 函数定义 ---
log_message() {
local level="$1"
local message="$2"
local ts
ts=$(date '+%Y-%m-%d %H:%M:%S')
[ ! -d "$LOG_DIR" ] && mkdir -p "$LOG_DIR" 2>/dev/null || true
echo "${ts} [${level}] ${message}" >> "$LOG_FILE"
if [[ "$level" == "ERROR" || "$level" == "FATAL" ]]; then
echo "${ts} [${level}] ${message}" >&2
fi
}
# 获取进程 PID (严格模式)
get_process_pid() {
local identifier="$1"
local pid=""
if [[ "$identifier" == *.jar ]]; then
# 【Java 策略】
# 必须匹配到 'java' 命令且命令行参数中包含该具体的 jar 文件名
# 使用 -f 是因为 java 进程的 cmdline 包含 jar 路径,但我们要确保 jar 名完全匹配
# 这里的正则确保 jar 文件名前后有边界 (空格或结束符),防止部分匹配
pid=$(pgrep -f "java.*${identifier}" | head -n 1)
else
# 【普通进程策略】
# 严格使用 -x (exact match),只匹配进程名完全等于 identifier 的进程
# 绝不进行模糊搜索
pid=$(pgrep -x "$identifier" | head -n 1)
fi
echo "$pid"
}
# 检查进程是否存在
is_process_running() {
local identifier="$1"
local pid
pid=$(get_process_pid "$identifier")
[ -n "$pid" ]
}
# 清理残留进程
cleanup_stale_process() {
local identifier="$1"
local pid
pid=$(get_process_pid "$identifier")
if [ -n "$pid" ]; then
log_message "WARN" "发现残留进程 '${identifier}' (PID: ${pid}),正在清理..."
# 尝试优雅停止
kill "$pid" 2>/dev/null || true
local count=0
while kill -0 "$pid" 2>/dev/null && [ $count -lt $STOP_WAIT_TIME ]; do
sleep 1
((count++))
done
# 强制杀死
if kill -0 "$pid" 2>/dev/null; then
log_message "WARN" "进程未响应,强制杀死 (PID: ${pid})"
kill -9 "$pid" 2>/dev/null || true
sleep 1
fi
log_message "INFO" "残留进程已清理完毕。"
fi
}
# 启动服务并验证
start_service() {
local name="$1"
local dir="$2"
local script="$3"
log_message "INFO" ">>> 开始启动服务: ${name}"
# 1. 环境校验
if [ ! -d "$dir" ]; then
log_message "ERROR" "启动失败:目录不存在 -> ${dir}"
return 1
fi
if [ ! -x "$script" ]; then
log_message "ERROR" "启动失败:脚本不可执行 -> ${script}"
return 1
fi
# 2. 清理可能占用的旧进程
cleanup_stale_process "$name"
# 3. 执行启动
cd "$dir" || { log_message "ERROR" "无法切换目录: ${dir}"; return 1; }
local startup_log="${dir}/startup_last.log"
# 使用 setsid 独立运行
setsid "$script" > "$startup_log" 2>&1 &
log_message "INFO" "启动命令已发送,进入状态轮询 (最多 ${STARTUP_TIMEOUT}s)..."
# 4. 轮询验证 (严格匹配)
local elapsed=0
while [ $elapsed -lt $STARTUP_TIMEOUT ]; do
sleep $POLL_INTERVAL
elapsed=$((elapsed + POLL_INTERVAL))
if is_process_running "$name"; then
local running_pid
running_pid=$(get_process_pid "$name")
log_message "INFO" "✅ 服务 '${name}' 启动成功 (PID: ${running_pid})"
return 0
fi
done
log_message "ERROR" "❌ 服务 '${name}' 启动超时!请检查日志: ${startup_log}"
return 1
}
# --- 主逻辑 ---
main() {
log_message "INFO" "========== 监控周期开始 =========="
for entry in "${SERVICES[@]}"; do
IFS=':' read -r identifier work_dir start_script <<< "$entry"
# 去除空格
identifier=$(echo "$identifier" | xargs)
work_dir=$(echo "$work_dir" | xargs)
start_script=$(echo "$start_script" | xargs)
[ -z "$identifier" ] && continue
log_message "DEBUG" "检测目标: ${identifier}"
if is_process_running "$identifier"; then
local pid
pid=$(get_process_pid "$identifier")
log_message "INFO" "正常: ${identifier} (PID: ${pid})"
else
log_message "WARN" "异常: ${identifier} 未运行,触发自愈..."
if ! start_service "$identifier" "$work_dir" "$start_script"; then
log_message "ERROR" "自愈失败: ${identifier},等待下一周期。"
# TODO: 此处可集成钉钉/邮件报警
fi
fi
done
log_message "INFO" "========== 监控周期结束 =========="
}
main || log_message "FATAL" "脚本执行异常退出"
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论