我是靠谱客的博主 无聊雨,最近开发中收集的这篇文章主要介绍JVM OOM监控加服务重启 Linux脚本,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

使用boot+dubbo框架开发的项目,在生产环境中正常运行两个月,在一天夜里发生宕机。
 
查看日志发现,JVM在凌晨1点钟做GC,尝试了多次,GC失败,直接抛出OOM异常。
 
异常日志
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "http-nio2-8801-Acceptor-0"
Exception in thread "SimplePauseDetectorThread_1" java.lang.OutOfMemoryError: GC overhead limit exceeded
java.lang.OutOfMemoryError: GC overhead limit exceeded

Exception in thread "http-nio2-8801-Acceptor-0" java.lang.OutOfMemoryError: Java heap space
    at java.nio.HeapByteBuffer.<init>(HeapByteBuffer.java:57)
02:35:02.282 [localhost-startStop-2-SendThread(iZwz9figezp50iy04h7ufyZ:2181)] WARN  o.a.z.ClientCnxn - [run,1112] - Client session timed out, have not heard from server in 46501ms for sessionid 0x100113ff5af0052
    at java.nio.ByteBuffer.allocate(ByteBuffer.java:335)
    at org.apache.tomcat.util.net.SocketBufferHandler.<init>(SocketBufferHandler.java:41)
    at org.apache.tomcat.util.net.Nio2Endpoint.setSocketOptions(Nio2Endpoint.java:312)
    at org.apache.tomcat.util.net.Nio2Endpoint$Acceptor.run(Nio2Endpoint.java:418)
    at java.lang.Thread.run(Thread.java:748)
02:35:03.959 [localhost-startStop-2-SendThread(iZwz9figezp50iy04h7ufyZ:2181)] INFO  o.a.z.ClientCnxn - [run,1160] - Client session timed out, have not heard from server in 46501ms for sessionid 0x100113ff5af0052, closing socket connection and attempting reconnect

上线时没有做内存快照,根据日志排查也没有发现直接问题。所以先做快照并且加入tomcat监控脚本。
 

通过配置JVM参数做内存快照,--XX:+HeapDumpOnOutOfMemoryError这个参数的含义是:当堆内存溢出时触发输出内存快照。通常搭配--XX:HeapDumpPath参数,指定快照的输出路径;同时可以指定快照文件名,默认快照文件名是 java_<pid>_<date>_<time>_heapdump.hprof。可以通过-XX:OnOutOfMemoryError参数指定发生内存溢出后执行的脚本。

--XX:+HeapDumpOnOutOfMemoryError
 
--XX:HeapDumpPath=/usr/local/hprof/
 
-XX:OnOutOfMemoryError='sh /usr/shellfile/xxxxx.sh'
注意:--XX:+HeapDumpOnOutOfMemoryError 触发条件是 java.lang.OutOfMemoryError: Java heap space。所以System.gc() 和fullggc并不会触发内存溢出快照,所以再写调试代码时,不要使用这两种方法。
 
配置tomcat的bin目录下的setenvsh文件
up-f950cffd324eb5451c90a980ae1af39916c.png
 
为了处理OOM后,通过-XX:OnOutOfMemoryError指定溢出后执行的脚本,处理服务异常。我最开始的计划是写一个restart的脚本,内存溢出后直接重启服务,短时间内当前节点可恢复运行,当然这是建立在有阿里云SLB心跳检查的基础上。
 
在写好脚本后,调试时发现无论我在一个脚本直接shutdown后kill 再重启,还是通过setsid和nohup …… & 命令做重启,都会出现一些进程把自己杀死,或者tomcat端口没有及时释放导致重启中端口占用的情况。所以最后决定 采用内存溢出后将tomcat shutdown并kill,通过linux crontab 做监控,发现访问异常,执行重启。
 
我们采用的 tomcat war包部署,所以使用tomcat的startup.sh 和 shutdown.sh
 
oom_exception.sh

#!/bin/sh

path='/usr/local/tomcatTest/'
shellfile=$path'bin/kill_tomcat.sh'
 
echo ===========OOM 执行强制关闭tomcat脚本==============
echo $shellfile
 
setsid /usr/shellfile/kill_tomcat.sh
 
echo ===========OOM 执行强制关闭tomcat脚本=============
kill_tomcat.sh
#!/bin/sh
p='/usr/tomcatTest'
#p=$(cd $(dirname $0)./; pwd)  
work=${p}'/work/'  
`rm -rf ${work}`  
tomcatpath=${p}'/bin'  
echo 'operate restart tomcat: '$tomcatpath  
pid=`ps aux | grep $tomcatpath | grep -v grep | grep -v retomcat | awk '{print $2}'`  
echo 'exist pid:'$pid  
 
#实时日志路径
curDate=$(date "+%Y-%m-%d")
realtimelog=${p}'/logs/catalina.'${curDate}'.out'
if [ ! -f "${realtimelog}"  ]
then
realtimelog=${p}'/logs/catalina.out'
fi
echo ===========realtimelog==============
echo ${realtimelog}
  
if [ -n "$pid" ]  
then  
{  
   echo ===========shutdown================  
   $tomcatpath'/shutdown.sh'  
   sleep 2  
   pid=`ps aux | grep $tomcatpath | grep -v grep | grep -v retomcat | awk '{print $2}'`  
   if [ -n "$pid" ]  
   then  
    {  
      sleep 2  
      echo ========kill tomcat begin==============  
      echo $pid  
      kill -9 $pid  
      echo ========kill tomcat end==============  
    }  
   fi
}  
else  
echo ===========not found pid==============  
fi  
 
执行完kill_tomcat.sh脚本后,此时这个tomcat就彻底被杀掉了。
 
然后,通过tomcat监控的脚本,监控tomcat被杀掉后重新启动,使用linux crontab延时执行。并且使用了钉钉群消息的方式通知相关人员。
 
tomcatMonitor.sh
#!/bin/sh
 
# func:系统监控 服务器宕机后,提醒并重启
 
# DEFINE
 
# 定义变量
hostname=`hostname`
nowtime=`date "+%Y-%m-%d-%H:%M:%S"`
host=127.0.0.1:6901
hostip=`hostname -I`
hostdes="$hostname $hostip"
 
# 定义要监控的页面地URL
WebUrl=http://$host/DoNOTremove.txt
 
# 日志输出
GetPageInfo=/tmp/TomcatMonitor.Info
TomcatMonitorLog=/tmp/TomcatMonitor.log
 
# 定义消息的标题
subject="[$hostname]系统运行监控"
subject2="[$hostdes]系统"
body=""
 
function SendMessageToDingding(){
        url="https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                UA="Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
                res=`curl -XPOST -s -L -H "Content-Type:application/json" -H "charset:utf-8" $url -d "
                {       "msgtype":"text",
                        "text":{"content":"$1n$2"},
                        "at":{
                        "atMobiles":["18888888888"],
                        "isAtAll":false
                        }
                }"`
        echo $res
}
 
Monitor()
{
echo "$hostdes"
        pidpath=/tmp/tcmonV1.pid      #定义pid文件
                if [ -f "$pidpath" ] ;then
                        echo "[$nowtime]脚本已经在执行,准备退出"
                        cat $pidpath
                exit 2
        else
                echo $$ >$pidpath #将当前Shell进程号记录到pid文件中
                echo "[$nowtime]脚本开始执行"
                cat $pidpath
 
                echo "[$nowtime][info]开始监测linux服务器..."
 
                res=`curl -XPOST -m 10 -s -L -H "Content-Type:application/json" -H "charset:utf-8" $WebUrl `
                echo $res
                if [[ $res =~ "hello running" ]] ;then
                        echo "[$nowtime][success]tomcat运行正常,接口访问正常......"
                else
                        echo "$nowtime][error]访问失败,系统准备自动重启"
                        body="[$nowtime][error]访问失败,系统准备自动重启"
                        SendMessageToDingding $subject $body
 
                        # 定义要重启的脚本路径
                        cd /usr/tomcatTest/bin/
                        ./restartup.sh
                        sleep 5m
 
                        # 检查是否重启成功
                        res=`curl -m 30 -XPOST -s -L -H "Content-Type:application/json" -H "charset:utf-8" $WebUrl `
                        echo $res
                        if [[ $res =~ "hello running" ]] ;then
                                echo "[$nowtime][success]tomcat重启成功,接口访问正常......"
                                body="[$nowtime][success]tomcat重启成功,接口访问正常......"
                                SendMessageToDingding $subject $body
                        else
                                echo "[$nowtime][error]tomcat启动可能失败或者重启超时,请管理员检查"
                                body="[$nowtime][error]tomcat启动可能失败或者重启超时,请管理员检查"
                                SendMessageToDingding $subject $body
                        fi
                fi
                echo "[$nowtime][info]结束监测linux服务器..."
                rm -f $pidpath
        fi
 
}
Monitor>>$TomcatMonitorLog
 测试直接执行脚本。( 如果执行新建脚本提示没有权限,可通过 chmod u+x *.sh ,为当前用户增加可执行权限)
up-23115cafd82f98874a074e3ee79947d010c.png
 
脚本执行,已检测到tomcat无法访问,执行重启
在重启过程中如果找不到 JRE_HOME ,需要重新配置一下,在setenv.sh中。本文setenv.sh 文件中已添加。
 
将通过crontab -e命令 ,当前用户新建一个任务文件,或者 vim /etc/crontab 打开全局的任务文件配置。
加入以下配置,配置是指每分钟执行,每延时10秒执行一次。以下配置结果是每10秒执行一次监控脚本。系统每隔1分钟会读取任务列表,修改后手动重启的意义不大。
* * * * * /bin/sh /usr/local/tomcatMonitor.sh
* * * * * sleep 10; /bin/sh /usr/local/tomcatMonitor.sh
* * * * * sleep 20; /bin/sh /usr/local/tomcatMonitor.sh
* * * * * sleep 30; /bin/sh /usr/local/tomcatMonitor.sh
* * * * * sleep 40; /bin/sh /usr/local/tomcatMonitor.sh
* * * * * sleep 50; /bin/sh /usr/local/tomcatMonitor.sh
查看脚本执行日志
up-1ff59dbbc48c2f4402f553faacfeb0a6dd7.png
 
可以看到脚本正常执行,日志打印也正常。接下来写一个会抛出OOM异常的接口,测试是否会是否后正常kill服务,监控脚本检测服务无法访问后重启tomcat。
 
@ResponseBody
@RequestMapping(value = "testHeap", method = RequestMethod.GET)
public Object testHeap() {

    List<byte[]> list = new ArrayList<>();
    Integer i = 0;
    while(true){
        list.add(new byte[10*1024*1024]);
        System.out.println("内存分配次数:" + (++i));
    }

}
经测试,监控状态正常。
 

最后

以上就是无聊雨为你收集整理的JVM OOM监控加服务重启 Linux脚本的全部内容,希望文章能够帮你解决JVM OOM监控加服务重启 Linux脚本所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(65)

评论列表共有 0 条评论

立即
投稿
返回
顶部