使用 Monit 监控 Java 进程:实现故障自恢复与 JVM 内存溢出自动处理

Admin 89 次阅读 本文阅读量 加载中… 编程语言

使用 Monit 监控 Java 进程:实现故障自恢复与 JVM 内存溢出自动处理

person road4918 schedule 2025-01-29 folder_open 运维 / Java / 物联网

最近在搞一个 Java 服务的运维,发现服务偶尔会挂掉,手动重启太麻烦了,于是研究了一下 Monit,发现这玩意儿真是神器!今天就来分享一下怎么用 Monit 监控 Java 进程,实现故障自动恢复,顺便还能处理 JVM 内存溢出的问题。

Monit 是什么?

简单来说,Monit 就是一个轻量级的监控工具,能帮你盯着系统里的进程、文件、目录啥的。如果发现哪个服务挂了,它能自动帮你重启,甚至还能发邮件告警。对于我们这些搞运维的来说,简直是省心利器!

本次目标

  1. 监控 Java 进程:确保服务挂了能自动重启。
  2. 处理 JVM 内存溢出:内存爆了的时候,自动杀掉进程,顺便生成 Heap Dump 文件,方便后续分析。
  3. 配置 Monit:让 Monit 帮我盯着多个 Java 服务,省得天天手动检查。

具体怎么搞?

1. 先写个停止脚本 stop.sh

首先,得有个脚本来停止 Java 进程。这个脚本的作用很简单:找到进程,杀掉它,顺便清理一下 PID 文件。

#!/bin/sh

APP_NAME="app_name"  # 这里改成你的应用名
SERVICE_DIR="/home/appuser/app/$APP_NAME"
PID=$APP_NAME\.pid

# 先尝试正常停止
kill `cat $SERVICE_DIR/$PID`
rm -rf $SERVICE_DIR/$PID
echo "=== stop $APP_NAME"
sleep 5

# 如果还没停掉,就强制杀死
P_ID=`ps -ef | grep -w "$APP_NAME" | grep -v "grep" | awk '{print $2}'`
if [ "$P_ID" == "" ]; then
  echo "=== $APP_NAME 进程已经停止了"
else
  echo "=== $APP_NAME 进程还在,PID是: $P_ID"
  echo "=== 开始强制杀死进程..."
  kill -9 $P_ID
fi

注意:记得把 APP_NAMESERVICE_DIR 改成你实际的值,然后给脚本加个执行权限:

chmod +x stop.sh

2. 改一下启动脚本 run.sh

接下来,得让 Java 进程在内存溢出的时候自动调用上面的 stop.sh。修改启动脚本,加上一些 JVM 参数:

JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/home/appuser/logs/jvmlogs_`date +%Y-%m-%d-%N` \
  -XX:ErrorFile=/home/appuser/logs/jvmlogs_`date +%Y-%m-%d-%N`/hs_error.log \
  -XX:OnOutOfMemoryError=\"/home/appuser/app/stop.sh\""

解释一下

  • -XX:+HeapDumpOnOutOfMemoryError:内存溢出时生成 Heap Dump 文件。
  • -XX:HeapDumpPath:指定 Heap Dump 文件的存放路径。
  • -XX:OnOutOfMemoryError:内存溢出时执行 stop.sh,杀掉进程。

完整的启动脚本示例

JAVA_OPTS='-Xms1024m -Xmx1024m -XX:MetaspaceSize=128m'
JAVA_OPTS="$JAVA_OPTS -Ddisconf.conf=/data/config/disconf.properties"
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/home/appuser/logs/jvmlogs_`date +%Y-%m-%d-%N` \
  -XX:ErrorFile=/home/appuser/logs/jvmlogs_`date +%Y-%m-%d-%N`/hs_error.log \
  -XX:OnOutOfMemoryError=\"/data/app/your-app/stop.sh\""
JAVA_OPTS="$JAVA_OPTS -Dpinpoint.applicationName=your-app-name"

#!/bin/sh
## java env
export JAVA_HOME=/data/app/jdk1.8.0_191
export JRE_HOME=$JAVA_HOME/jre

APP_NAME=your-app
SERVICE_DIR=/data/app/your-app
JAR_NAME=$APP_NAME\.jar
PID=$APP_NAME\.pid

cd $SERVICE_DIR

case "$1" in
    deploy)
        $0 stop
        sleep 2
        echo "=== deploy $APP_NAME"
        cd $SERVICE_DIR
        cp  $JAR_NAME $JAR_NAME.$(date +%Y%m%d)
        rm -f $JAR_NAME
        cp /home/appuser/target/$JAR_NAME .
        $0 start
        ;;
    start)
        nohup java $JAVA_OPTS -jar $JAR_NAME >/dev/null 2>&1 &
        echo $! > $SERVICE_DIR/$PID
        echo "=== start $APP_NAME"
        ;;
    stop)
        kill `cat $SERVICE_DIR/$PID`
        rm -rf $SERVICE_DIR/$PID
        echo "=== stop $APP_NAME"
        sleep 5
        P_ID=`ps -ef | grep -w "$APP_NAME" | grep -v "grep" | awk '{print $2}'`
        if [ "$P_ID" == "" ]; then
            echo "=== $APP_NAME process not exists or stop success"
        else
            echo "=== $APP_NAME process pid is:$P_ID"
            echo "=== begin kill $APP_NAME process, pid is:$P_ID"
            kill -9 $P_ID
        fi
        ;;
    restart)
        $0 stop
        sleep 2
        $0 start
        echo "=== restart $APP_NAME"
        ;;
    *)
        $0 stop
        sleep 2
        $0 start
        ;;
esac

exit 0
warning
安全提示:生产环境中请不要在脚本中硬编码密码或敏感配置。建议将应用名、服务目录、JVM 参数等抽取为环境变量或外部配置文件,并通过配置中心统一管理。

3. 安装和配置 Monit

3.1 安装 Monit

如果你的系统还没装 Monit,可以用以下命令安装:

#!/bin/sh

DIR=/home/appuser/monit
sudo yum install monit -y

if [ ! -d $DIR ]; then
    mkdir -p $DIR
    sudo cp /etc/monitrc $DIR/ && sudo cp -r /etc/monit.d $DIR/
    sudo chown -R appuser:appuser $DIR
    sed -i "s#/var/log#"$DIR"#" /$DIR/monit.d/logging
    sed -i "s#include /etc#include "$DIR"#" /$DIR/monitrc
else
    echo ""$DIR" is already exists!"
fi

3.2 配置 Monit 日志

Monit 的日志很重要,出了问题可以查。我们指定一下日志文件的位置:

# cat monit.d/logging

# log to monit.log
set logfile /data/appuser/monit/m.log

3.3 配置 Monit 监控规则

接下来就是重头戏了,配置 Monit 来监控 Java 进程。比如我们有多个 Java 服务,可以这样写:

# 监控 iot-open 服务
check process iot-open with pidfile /data/app/iot-open/iot-open.pid every 11 cycles
    start program = "/bin/bash -c '/data/app/iot-open/run.sh start'" with timeout 30 seconds
    stop program = "/bin/bash -c '/data/app/iot-open/run.sh stop'"
    if failed port 7171 type tcp for 11 cycles then restart
    if changed pid then alert

# 监控 iot-share-api 服务
check process iot-share-api with pidfile /data/app/iot-share-api/iot-share.pid every 2 cycles
    start program = "/bin/bash -c '/data/app/iot-share-api/run.sh start'" with timeout 80 seconds
    stop program = "/bin/bash -c '/data/app/iot-share-api/run.sh stop'"
    if changed pid then alert

# 其他服务类似...

说明

  • 每个 check process 块对应一个服务的监控规则。
  • start programstop program 分别是启动和停止脚本。
  • if failed port 是检测端口是否可用,如果失败了就重启服务。

4. 重新加载 Monit 配置

配置写好了,别忘了让 Monit 重新加载一下:

# 测试配置文件是否正确
monit -c /data/appuser/monit/monitrc -t

# 启动
monit -c /data/appuser/monit/monitrc

# 退出
monit -c /data/appuser/monit/monitrc quit

# 重新加载配置
monit -c /data/appuser/monit/monitrc reload

然后 Monit 就会开始监控你的服务了。如果某个服务挂了,它会自动帮你重启,省得你手动操作。

总结

用了 Monit 之后,感觉整个人都轻松了不少。Java 服务挂了?Monit 帮你重启!内存溢出了?Monit 帮你杀掉进程,还能生成 Heap Dump 文件!简直是运维小能手。

如果你也在为 Java 服务的稳定性发愁,不妨试试 Monit,真的香!有啥问题或者更好的建议,欢迎留言讨论。