你见过java程序自毁(kill)吗?我踩过

3,727 阅读5分钟

“我正在参加「掘金·启航计划」”

前言

  • 在线音乐戳我呀!
  • 音乐博客源码上线啦!
  • 之前专栏奔向Linux、Docker前几篇讲了Docker的安装部署,操作算是比较简单。
  • 你见过java程序自毁(kill)吗?我遇到过。
  • 今天,兴致勃勃想登陆在线音乐听听周董的《稻香》,结果,发现请求音乐接口失败(音乐博客是由java服务提供的第三方服务),于是马上进入服务器看下日志,进行了排查之路。
  • 接下来将分享如何在服务器排查java服务无缘无故被kill掉的问题,这个问题非常有意思,主要是很难排查,自己被kill掉,一五一十盘出。
  • Are you ready ?

怀念那年夏天,漂流~

16.jpg

Java无缘无故被kill掉

发现接口调用失败,排查之后发现程序已被kill停止。

环境

  • Java:Springboot

  • 服务器:java是在Docker中运行

一、jar包无缘无故被kill?

动物群体自毁我见过,但jar包无缘无故被kill掉,也没有报错信息,这就很难排查了。

1.png

看到最后一行,Killed。

1.1 事务背锅?

🙋 想一想,我们有没有定时器脚本,或者程序的定时器关掉服务呢?

🙋🏻‍♂️ 没有。那如果没有,就是先从程序自身找问题了。

从上图我们有一句关键的输出语句:closing non transactional SqlSession.

得知一点,说我用了事务,但是我程序中并没有开启事务,所以就报错了。

我保存删除接口用了事务。

3.png

现在我们来开启事务。

因为我们使用的是Springboot,所以加上注解即开启事务;若是之前的xml项目,加的就是配置了。

4.png

1.2 重复注入

接下来我们神采飞扬打完jar包丢到服务器上,运行一下。

结果,报错了。

5.png

我们定位到这个类看看代码。

6.png

@service重复注入(重复注册了,你在实现类里面已经注入了),删掉。

7.png

1.3 事务语法写的有问题?jdk、cglib?

好,我们删掉@service之后,又笑嘻嘻的丢到服务器上,准备run一下。

又又又报错。

我们上面开启事务加的是:@EnableTransactionManagement()

并没有加参数,我们添加参数试一下。

@EnableTransactionManagement(proxyTargetClass=True))

因为这个注解是采用cglib动态代理方式实现的  然后你代码中加了@Transactional,那个注解是实现类上实现的。

8.png

实现了类就走jdk,但是我用了@Transactional注解就走cglib,所以要加开启事务为cglib。

如果我实现类了,并且用了@Transactional注解,就走cgllb,但我又没有开启事务(第一开始没有加参数默认走jdk),所以报错。

没实现类的情况下就走jdk动态代理;开启事务就默认都走cglib动态代理。

简而言之就是我们开启的事务动态代理方式错了。

1.4 真相大白,解决方法

我们已经分析问题所在,接下来将解决问题。

Springboot在3.2之后就默认引入了cgllb,不用我们自己引入。

@EnableTransactionManagement(proxyTargetClass=True))

9.png

验证方法:查看事务是否有生效?
在插入之后写个报错的语句1/0,看看数据有没有插入,有就事务不生效,无就生效。

二、不好意思,我(服务器)又来kill你了

你以为,功成圆满了吗?

事情并没有结束。

事情发生在重启服务的一天后,我又兴致勃勃想登陆在线音乐听听周董的《稻香》,又被kill。

19.jpeg

11.png

很巧的是,刚刚好24小时,整整一天。

这就很像某种定时器做的事情。

三、linux系统测试排查

带着这个疑惑,因为我们是运行在docker上的,我想试一下linux环境会不会也是如此(先排除环境)

于是我开始测试一下,linux,安装java环境。  

在java里面配置日志输出:

13.png

运行走起:

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的问题。

其实是内存的问题导致服务被中止。

我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车

如果对您有帮助,你的点赞是我前进的润滑剂。

以往推荐

是谁让oracle、mysql同时崩溃的!

愤怒,mysql被疯狂攻击,我做了这几件事!

Vue-Cli3搭建组件库

Vue实现动态路由(和面试官吹项目亮点)

VuePress搭建项目组件文档

相关文献

linux下启动oracle报Connected to an idle instance

Linux系统下安装Java环境

原文链接

juejin.cn/post/720463…