Linux服务器定时监测服务脚本

这两天我们有个服务出现了很严重的内存泄漏问题,本来按说既然是内存泄漏问题那我们就找到内存泄漏原因然后解决掉就行了,但是因为那个服务不是我写的,然后现在又很忙抽不出那么多时间,所以就先用临时的方法补救一下,写个监测脚本发现不对就自动重启

脚本内容如下(脚本名称:monitor.sh)
注意我这里是根据 CPU 使用率判断的,因为这个服务即使内存泄漏很严重但是服务本身没有挂掉还在运行,只是请求进不去了
下面脚本加了一些内容,因为我发现有的服务器上环境变量不对启动不了 jar 包,还有就是有的程序启动的时候会有一段时间 CPU 占比非常大,所以又加了一个冷却时间,防止一直重启

#!/bin/bash

APP_HOME="/data/application/app"
JAR_NAME="app-1.0.jar"
LOG_FILE="$APP_HOME/monitor.log"
RESTART_RECORD="$APP_HOME/.last_restart_time"

# 设置 Java 环境
export JAVA_HOME=/data/midsoftware/ali_jdk_8.16.17
export PATH=$JAVA_HOME/bin:$PATH

# 进入应用目录
cd $APP_HOME || { echo "Failed to cd $APP_HOME"; exit 1; }

PID=$(ps aux | grep "$JAR_NAME" | grep -v grep | awk '{print $2}')

CURRENT_TIME=$(date +%s)
COOLDOWN=150   # 150秒冷却时间

LAST_RESTART=0
if [ -f "$RESTART_RECORD" ]; then
    LAST_RESTART=$(cat "$RESTART_RECORD")
fi

if [ -z "$PID" ]; then
    # 服务未运行,启动
    sh startup.sh
    echo "$(date '+%F %T') Service not running, starting..." >> $LOG_FILE
    date +%s > "$RESTART_RECORD"   # 记录启动时间
else
    # 检查 CPU
    # CPU=$(ps -p $PID -o %cpu= | awk '{print int($1)}')
    CPU=$(top -b -n1 -p $PID | tail -n +8 | awk '{print int($9)}')
    echo "$(date '+%F %T') PID=$PID CPU=${CPU}%" >> $LOG_FILE
    # 注意这个80,指的是单核的80,如果你是8核的满载是800,这里可以加大
    CPU_THRESHOLD=80

    TIME_DIFF=$(( CURRENT_TIME - LAST_RESTART ))

    if [ "$CPU" -gt "$CPU_THRESHOLD" ]; then
        if [ "$TIME_DIFF" -lt "$COOLDOWN" ]; then
            echo "$(date '+%F %T') CPU exceed $CPU_THRESHOLD%, but within cooldown period, skipping restart." >> $LOG_FILE
        else
            echo "$(date '+%F %T') CPU exceed $CPU_THRESHOLD%, restarting service..." >> $LOG_FILE
            sh shutdown.sh
            sleep 5
            sh startup.sh
            echo "$(date '+%F %T') Restarted service" >> $LOG_FILE
            date +%s > "$RESTART_RECORD"   # 更新重启时间
        fi
    fi
fi

加上权限

chmod +x /data/application/app/monitor.sh

然后我们把它加到定时任务里面去

crontab -e

在里面加入一行(注意自己的脚本路径和名称)

* * * * * /data/application/app/monitor.sh >> /data/application/app/monitor.log 2>&1

上面的是每分钟执行一次,服务器上的定时任务最小的粒度就是一分钟了,如果想更细的粒度比如每十秒检查一次可以写成下面这样的

* * * * * /data/application/app/monitor.sh >> /data/application/app/monitor.log 2>&1
* * * * * sleep 10; /data/application/app/monitor.sh >> /data/application/app/monitor.log 2>&1
* * * * * sleep 20; /data/application/app/monitor.sh >> /data/application/app/monitor.log 2>&1
* * * * * sleep 30; /data/application/app/monitor.sh >> /data/application/app/monitor.log 2>&1
* * * * * sleep 40; /data/application/app/monitor.sh >> /data/application/app/monitor.log 2>&1
* * * * * sleep 50; /data/application/app/monitor.sh >> /data/application/app/monitor.log 2>&1

检查一下看是否添加成功,如果下面命令里面能看到你家的那条记录就说明成功了

crontab -l

等一两分钟我们再去看下日志(注意自己的路径)

tail -f /data/application/app/monitor.log

能看到下面这样的

root@ecs-v2:/data/application/app# tail -f /data/application/app/monitor.log
2025-08-22 09:36:01 PID=1736316 CPU=22%
2025-08-22 09:37:01 PID=1736316 CPU=22%
2025-08-22 09:38:01 PID=1736316 CPU=22%
2025-08-22 09:39:01 PID=1736316 CPU=22%
2025-08-22 09:40:01 PID=1736316 CPU=22%

还有那种服务直接挂了重启的脚本,其它的和上面一样,加入到定时任务里面就可以了

#!/bin/bash
APP_HOME=/data/application/app
JAR_NAME=app-1.0.jar
START_SCRIPT=$APP_HOME/startup.sh

# 找到进程 PID
PID=$(ps aux | grep $JAR_NAME | grep -v grep | awk '{print $2}')

cd $APP_HOME

if [ -z "$PID" ]; then
    sh $START_SCRIPT
    echo "服务已挂掉,已自动重启,时间:`date '+%Y-%m-%d %T'`" >> $APP_HOME/restart_record.log
fi

2025.9.17补充
这几条一直在排查这个内存泄漏问题,中间调整了这个脚本,加了一些检测的功能,这里一并放出来

#!/bin/bash

APP_HOME="/data/application/web-report-controller"
JAR_NAME="web-report-controller-1.0.jar"
LOG_FILE="$APP_HOME/monitor.log"
RESTART_RECORD="$APP_HOME/.last_restart_time"
DUMP_DIR="$APP_HOME/dumps"

# 设置 Java 环境
export JAVA_HOME=/data/midsoftware/ali_jdk_8.16.17
export PATH=$JAVA_HOME/bin:$PATH

mkdir -p "$DUMP_DIR"

cd $APP_HOME || { echo "Failed to cd $APP_HOME"; exit 1; }

PID=$(ps aux | grep "$JAR_NAME" | grep -v grep | awk '{print $2}')

CURRENT_TIME=$(date +%s)
COOLDOWN=150

LAST_RESTART=0
if [ -f "$RESTART_RECORD" ]; then
    LAST_RESTART=$(cat "$RESTART_RECORD")
fi

if [ -z "$PID" ]; then
    sh startup.sh
    echo "$(date '+%F %T') Service not running, starting..." >> $LOG_FILE
    date +%s > "$RESTART_RECORD"
else
    # 使用 /proc 文件系统获取准确的内存信息
    if [ -f "/proc/$PID/status" ]; then
        RSS_KB=$(grep VmRSS /proc/$PID/status | awk '{print $2}')
        VSZ_KB=$(grep VmSize /proc/$PID/status | awk '{print $2}')
        RSS_MB=$((RSS_KB / 1024))
        VSZ_MB=$((VSZ_KB / 1024))
        
        # 使用top获取实时CPU
        CPU=$(top -b -n1 -p $PID 2>/dev/null | tail -1 | awk '{print int($9)}')
        # 使用ps获取内存百分比
        MEM=$(ps -p $PID -o %mem --no-headers 2>/dev/null | awk '{print int($1)}')
        
        echo "$(date '+%F %T') PID=$PID CPU=${CPU}% MEM=${MEM}% RSS=${RSS_MB}MB VSZ=${VSZ_MB}MB" >> $LOG_FILE

			 # 注意这个300,指的是单核的80,如果你是8核的满载是800,这里可以加大
        CPU_THRESHOLD=300
        TIME_DIFF=$(( CURRENT_TIME - LAST_RESTART ))

        if [ "$CPU" -gt "$CPU_THRESHOLD" ]; then
            JSTACK_FILE="$DUMP_DIR/jstack_${PID}_$(date +%F_%H-%M-%S).log"
            jstack $PID > "$JSTACK_FILE" 2>&1
            echo "$(date '+%F %T') High CPU detected, jstack saved to $JSTACK_FILE" >> $LOG_FILE

            if [ "$TIME_DIFF" -lt "$COOLDOWN" ]; then
                echo "$(date '+%F %T') CPU exceed ${CPU_THRESHOLD}%, but within cooldown period, skipping restart." >> $LOG_FILE
            else
                echo "$(date '+%F %T') CPU exceed ${CPU_THRESHOLD}%, restarting service..." >> $LOG_FILE
                sh shutdown.sh
                sleep 5
                sh startup.sh
                echo "$(date '+%F %T') Restarted service" >> $LOG_FILE
                date +%s > "$RESTART_RECORD"
            fi
        fi
    else
        echo "$(date '+%F %T') PID $PID not found in /proc, process may have terminated" >> $LOG_FILE
    fi
fi

里面会输出这样日志,还会生成 jstack_4019781_2025-09-17_01-29-21.log 线程状态记录,根据生成的这些文件我这边已经找到了问题原因,就是 GC 太频繁导致的,里面一堆的 GC 线程在执行
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子非衣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值