shell脚本监控SpringBoot服务健康状态并重启
创始人
2024-04-07 23:50:06
0

问题

  • 综合客户反馈的现场情况,服务前一天可以使用正常,第二天早晨发现报错; 初步怀疑是晚上什么操作导致的,大概是定时任务
  • 查看服务日志,综合几天日志,发现每晚都会执行一个日志清除的定时任务scheduledtask.CleanLogJob
{"log":"\u001b[2m2022-10-30 00:00:00.001\u001b[0;39m \u001b[32m INFO\u001b[0;39m \u001b[35m1\u001b[0;39m \u001b[2m---\u001b[0;39m \u001b[2m[ne-scheduling-1]\u001b[0;39m \u001b[36mc.newatc.api.scheduledtask.CleanLogJob  \u001b[0;39m \u001b[2m:\u001b[0;39m ----开启定时清理日志文件----2022-10-29T16:00:00.001343Z\n","stream":"stdout","time":"2022-10-29T16:00:00.002200283Z"}
{"log":"\n","stream":"stderr","time":"2022-10-29T16:00:32.158196254Z"}
{"log":"Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread \"Hikari housekeeper\"\n","stream":"stderr","time":"2022-10-29T16:00:32.158216755Z"}
{"log":"\u001b[2m2022-10-30 00:01:22.799\u001b[0;39m \u001b[31mERROR\u001b[0;39m \u001b[35m1\u001b[0;39m \u001b[2m---\u001b[0;39m \u001b[2m[ne-scheduling-1]\u001b[0;39m \u001b[36mo.s.s.s.TaskUtils$LoggingErrorHandler   \u001b[0;39m \u001b[2m:\u001b[0;39m Unexpected error occurred in scheduled task\n","stream":"stdout","time":"2022-10-29T16:01:22.800399506Z"}
{"log":"\n","stream":"stdout","time":"2022-10-29T16:01:22.800414807Z"}
{"log":"java.lang.OutOfMemoryError: Java heap space\n","stream":"stdout","time":"2022-10-29T16:01:22.800417418Z"}
{"log":"\n","stream":"stdout","time":"2022-10-29T16:01:22.800419609Z"}
  • 定时任代码是先根据当前日期查询7天前的所有数据,再全部删除。当某一天数据量特别大,无法全部加载到内存时,就会内存溢出,程序就会假死
/*** 每天零点定时清理日志*/@Scheduled(cron = "0 0 0 * * ?")public void deleteLog(){log.info("----开启定时清理日志文件----{}", Instant.now());try{SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Calendar c = Calendar.getInstance();//过去七天c.setTime(new Date());c.add(Calendar.DATE, -7);String day = format.format(c.getTime());List opLogList = findOpLogByDayNum(day);opLogRepository.deleteAll(opLogList);List signalAlarmList = findSignalAlarmByDayNum(day);signalAlarmRepository.deleteAll(signalAlarmList);}catch (Exception e){log.error("deleteLog error..", e);}}
  • 定时任务执行后,内存溢出java.lang.OutOfMemoryError: Java heap space。查看服务的jvm配置,只分配了 512M 内存,对于大量数据读入内存,确实不够用
  • 查看代码提交记录,综合更新部署时间和问题出现时间,基本确定是此问题
  • 让运维人员协助查询了数据库,发现每天新增10万条信号机报警日志,仍有13天(100多万条)的数据存在,确实是删除日志时内存溢出失败

分析

  • 数据量问题确实是到客户现场,有实际这么多设备和用户才暴露出来的,公司测试无法进行此种场景的压力测试,只能把这款产品的第一个用户作为小白鼠了
  • SpringBoot服务因异常宕机或假死时,不再提供服务,会从注册中心掉线,并且整个应用就不可用了
  • 如果只是某些菜单功能不可用,对于客户来说还可以接受,毕竟试运行阶段,有点小问题客户也能理解
  • 如果是整个服务宕机,界面打不开,客户就会对系统的稳定性产生怀疑,印象会不太好
  • 或者说,某些导致宕机的问题,如果及时处理了,客户就暂时发现不了这个问题,就能减少对客户的负面印象

处理

  • 针对于此,写了个shell脚本,监控SpringBoot服务暴露的端点,服务不可用时,进行重启操作
  • 为了防止网络波动造成的影响,重试三次请求,都得不到健康的结果,才认为不可用
  • 具体脚本如下:
#!/bin/bash# 检查服务是否可用,判断服务状态
function checkHealth() {currTime=`date +"%Y-%m-%d %H:%M:%S"`echo "$currTime $@"; # 以列表的方式一次性打印所有的参数ConsulResult=$(curl -s --connect-timeout 500 --max-time 2  http://$1/actuator/health )target="UP"local health=1if [[ $ConsulResult =~ $target ]]thenecho "$2结果健康";elsesleep 1s;ConsulResult=$(curl -s --connect-timeout 500 --max-time 2  http://$1/actuator/health )if [[ $ConsulResult =~ $target ]]thenecho "$2结果健康";elsesleep 1s;ConsulResult=$(curl -s --connect-timeout 500 --max-time 2  http://$1/actuator/health )if [[ $ConsulResult =~ $target ]]thenecho "$2结果健康";elsehealth=0;echo "$2 结果不健康";fififireturn $health;
}# 检查服务健康状态,并处理异常
function dealCheckResult() {ipport=$1name=$2echo "start $name ======================="checkHealth $ipport $namehealth=$?if [[ $health -eq 1 ]]thenecho "$name 检查完毕,无问题!"elseecho "重启 $name 预计2分钟"# 重启服务docker stop $namesleep 5sdocker start $namesleep 120s# 重新检查一次是否启动成功,不成功则再休眠60scheckHealth $ipport $namehealth=$?currTime=`date +"%Y-%m-%d %H:%M:%S"`if [[ $health -eq 1 ]];thenecho "$currTime $name 服务已重启完成"elseecho "$currTime $name 服务尚未重启完成,再休眠60s等待"sleep 60sfifiecho "end $name 所有处理完毕==========="
}# 获取系统启动时间
uptime=$(cat /proc/uptime)
echo "uptime: $uptime"
uptimeArr=(${uptime//./ })
startTime=${uptimeArr[0]}
echo $startTime
a=`expr $startTime + 1`
b=1800
# 系统启动30分钟内不执行
if [[ "$a" -lt "$b" ]];
thenecho "系统启动30分钟内不执行"exit
fi
echo "启动时间为$startTime秒,正常执行脚本"echo "先关闭定时任务"
/sbin/service crond stopipport1=172.16.1.122:8181
name1=datacenter
dealCheckResult $ipport1 $name1ipport2=172.16.1.122:8282
name2=unit
dealCheckResult $ipport2 $name2ipport3=172.16.1.122:8383
name3=core
dealCheckResult $ipport3 $name3echo "重新启动定时任务"
/sbin/service crond start

相关内容

热门资讯

【手写 Promise 源码】... 一,前言 目前,以下专栏已完结(初版完结,持...
利用Golang可选参数实现可... 本文讨论Golang函数可选参数及函数类型,以及如何利用可选函数类型实现可选模式。同时...
【学习笔记】[AGC040F]... 我是丝薄因为我不会GF 考虑纯组合意义的推导。因为我真的不会数学啊 设(x,d)(x,d)(x,d)...
java线程中断 interr... 1.  线程的状态: new -> 创建完线程,但是还没有启动runna...
16 | 如何做好面试后的复盘... 前言 前言:将经验转换为能力,就需要从经历的事情上总结复盘。 文章目...
Python基础学习七 类 Python从设计之初就已经是一门面向对象的语言,正因为如此,在Py...
day 19 暴力枚举 注意:用long long 存答案P2241 统计方形(数据加强版&#x...
嵌入式理论复习题库(期末考试版... 一、填空题15*1=15 1. 要下载并调试 STM32 程序,可采用   ...
OSG三维渲染引擎编程学习之二... 目录 第三章:OSG场景组织 3.6 Transform变换节点 第三章:OSG场景组织 ...
cmake 04 使用 pyt... 本文目标 使用 python 写一个管理 cmake 工程的 cli 程序 参考 Python CL...