你见过java程序自毁(kill)吗?我踩过
前言
- 在线音乐戳我呀!
- 音乐博客源码上线啦!
- 之前专栏奔向Linux、Docker前几篇讲了Docker的安装部署,操作算是比较简单。
- 你见过java程序自毁(kill)吗?我遇到过。
- 今天,兴致勃勃想登陆在线音乐听听周董的《稻香》,结果,发现请求音乐接口失败(音乐博客是由java服务提供的第三方服务),于是马上进入服务器看下日志,进行了排查之路。
- 接下来将分享如何在服务器排查java服务无缘无故被kill掉的问题,这个问题非常有意思,主要是很难排查,自己被kill掉,一五一十盘出。
- Are you ready ?
怀念那年夏天,漂流~
Java无缘无故被kill掉
发现接口调用失败,排查之后发现程序已被kill停止。
环境
-
Java:Springboot
-
服务器:java是在Docker中运行
一、jar包无缘无故被kill?
动物群体自毁我见过,但jar包无缘无故被kill掉,也没有报错信息,这就很难排查了。
看到最后一行,Killed。
1.1 事务背锅?
🙋 想一想,我们有没有定时器脚本,或者程序的定时器关掉服务呢?
🙋🏻♂️ 没有。那如果没有,就是先从程序自身找问题了。
从上图我们有一句关键的输出语句:closing non transactional SqlSession.
得知一点,说我用了事务,但是我程序中并没有开启事务,所以就报错了。
我保存删除接口用了事务。
现在我们来开启事务。
因为我们使用的是Springboot,所以加上注解即开启事务;若是之前的xml项目,加的就是配置了。
1.2 重复注入
接下来我们神采飞扬打完jar包丢到服务器上,运行一下。
结果,报错了。
我们定位到这个类看看代码。
@service重复注入(重复注册了,你在实现类里面已经注入了),删掉。
1.3 事务语法写的有问题?jdk、cglib?
好,我们删掉@service之后,又笑嘻嘻的丢到服务器上,准备run一下。
又又又报错。
我们上面开启事务加的是:@EnableTransactionManagement()
并没有加参数,我们添加参数试一下。
@EnableTransactionManagement(proxyTargetClass=True))
因为这个注解是采用cglib
动态代理方式实现的 然后你代码中加了@Transactional,那个注解是实现类上实现的。
实现了类就走jdk,但是我用了@Transactional注解就走cglib,所以要加开启事务为cglib。
如果我实现类了,并且用了@Transactional注解,就走cgllb,但我又没有开启事务(第一开始没有加参数默认走jdk),所以报错。
没实现类的情况下就走jdk动态代理;开启事务就默认都走cglib动态代理。
简而言之就是我们开启的事务动态代理方式错了。
1.4 真相大白,解决方法
我们已经分析问题所在,接下来将解决问题。
Springboot在3.2之后就默认引入了cgllb,不用我们自己引入。
@EnableTransactionManagement(proxyTargetClass=True))
验证方法:查看事务是否有生效?在插入之后写个报错的语句1/0,看看数据有没有插入,有就事务不生效,无就生效。
二、不好意思,我(服务器)又来kill你了
你以为,功成圆满了吗?
事情并没有结束。
事情发生在重启服务的一天后,我又兴致勃勃想登陆在线音乐听听周董的《稻香》,又被kill。
很巧的是,刚刚好24小时,整整一天。
这就很像某种定时器做的事情。
三、linux系统测试排查
带着这个疑惑,因为我们是运行在docker上的,我想试一下linux环境会不会也是如此(先排除环境)
于是我开始测试一下,linux,安装java环境。
在java里面配置日志输出:
运行走起:
Java -jar bkapi-0.0.1.jar --server.port=6666
发现时间一到,还是会被kill。
四、解决:内存不足。
最后发现真正的原因:内存不足。
这个应该是java里面的机制,就算你内存不够了,他等到24小时才会自动去kill掉你程序。
我也不想自毁呀,可内存不让。
🙋那如果java机制不是24h,也不可能这么准时,刚好24就kill,他内存不够,可以3小时就kill,但他并没有这样子。
所以我们要为程序分配内存,于是要写个脚本sh文件。
在jar文件同级新建startup.sh。
#!/bin/sh
JAVA_OPTS="-server -Xms512m -Xmx512m -Xmn256m -XX:PermSize=128m -XX:MaxPermSize=256m -XX:MetaspaceSize=128M -XX:-UseGCOverheadLimit -Djava.rmi.server.hostname=127.0.0.1 -Djava.net.preferIPv4Stack=true -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=8 -XX:+PrintGCDetails -Xloggc:./gc.log"
nohup java $JAVA_OPTS -DappName=bkapi -jar bkapi-0.0.1.jar --server.port=6666 --spring.profiles.active=prod 2>&1 > /dev/null &
因为我内存不够,所以分配512m,我只有1g,不能分配太多,拿同事的配置2g,结果在日志看到内存不足。
分享几个有用的命令:
# 查看日志
Tail -200f logs/appservice.log
# 杀死进程
Kill -9 5452
# 查看进程
ps -ef|grep java
后记
那么有人可能问了,看你程序是2020年写的,那之前没用过吗?
有用过。
那为什么之前不会报错,不会被kill掉呢?
因为之前是部署在Windows,而现在换环境了,部署在Docker上。
这还有关系吗?
一开始猜测不知道是不是Docker没有配置好,导致程序被kill。
所以才放在Linux上测试一下。
当然,像今天oracle发生突然连接不了,是因为实例被停掉了。
我们表面确实是把问题解决掉了,但他为什么会停掉呢?这是一个可以深思的问题,很多时候,我们总是看到问题的表面,直到有一天我怀疑是不是docker的问题。
其实是内存的问题导致服务被中止。
如果对您有帮助,你的点赞是我前进的润滑剂。
以往推荐
相关文献
linux下启动oracle报Connected to an idle instance
原文链接
转载自:https://juejin.cn/post/7204636361197207609