最近在搞一个Java服务的运维,发现服务偶尔会挂掉,手动重启太麻烦了,于是研究了一下 Monit,发现这玩意儿真是神器!今天就来分享一下怎么用Monit监控Java进程,实现故障自动恢复,顺便还能处理JVM内存溢出的问题。
Monit是啥?
简单来说,Monit就是一个轻量级的监控工具,能帮你盯着系统里的进程、文件、目录啥的。如果发现哪个服务挂了,它能自动帮你重启,甚至还能发邮件告警。对于我们这些搞运维的来说,简直是省心利器!
我要干啥?
这次的目标很简单:
-
监控Java进程:确保服务挂了能自动重启。
-
处理JVM内存溢出:内存爆了的时候,自动杀掉进程,顺便生成Heap Dump文件,方便后续分析。
-
配置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_NAME 和 SERVICE_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/ems-fec-ds/stop.sh\""
JAVA_OPTS="$JAVA_OPTS -Dpinpoint.applicationName=online-ems-fec-ds"
#JAVA_OPTS="$JAVA_OPTS -Dio.netty.leakDetectionLevel=advanced"
#JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=172.16.168.103 -Dcom.sun.management.jmxremote.port=18888 -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.managementote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
#JAVA_OPTS="$JAVA_OPTS -XX:+DisableExplicitGC"
#JAVA_OPTS="$JAVA_OPTS -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+PrintGCApplicationStoppedTime -Xloggc:gc.log"
#!/bin/sh
## java env
export JAVA_HOME=/data/app/jdk1.8.0_191
export JRE_HOME=$JAVA_HOME/jre
APP_NAME=water-ds
SERVICE_DIR=/data/app/ems-fec-ds
JAR_NAME=$APP_NAME\.jar
PID=$APP_NAME\.pid
cd $SERVICE_DIR
#mv $JAR_NAME $JAR_NAME.$(date +%Y%m%d)
#rm -f $JAR_NAME
#cp /home/appuser/target/$JAR_NAME .
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 >$APP_NAME\.out 2>&1 &
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"
;;
*)
## restart
$0 stop
sleep 2
$0 start
;;
esac
exit 0
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的日志很重要,出了问题可以查。我们指定一下日志文件的位置:
[appuser@daqin monit]$ cat monit.d/logging
# log to monit.log
set logfile /data/appuser/monit/m.log
3.3 配置Monit监控规则
接下来就是重头戏了,配置Monit来监控Java进程。比如我们有多个Java服务,可以这样写:
# 监控 charging-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
# 监控 ems-share-api 服务
check process iot-share-api with pidfile /data/app/iot-share-api/ems-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 program和stop 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,真的香!有啥问题或者更好的建议,欢迎留言讨论
1289

被折叠的 条评论
为什么被折叠?



