目录
(三)java -jar app.jar\app.war命令启动过程
(四)"com.mysql.jdbc.MySQLConnection"类加载过程
一、背景
最近有个项目需要进行容器化改造,将原主机tomcat部署方式迁移到k8s平台,部署到docker容器中。为了方便docker镜像处理,不再使用外置tomcat容器部署应用,改用jar包方式通过java -jar命令直接运行应用。
一般来说springboot应用常用的打包方式有两种:jar、war,一般来说我们根据项目是否有使用外置tomcat容器决定使用哪种打包方式:
- 使用外置tomcat容器,使用war方式,可根据需求使用任意版本的tomcat容器;
- 使用是springboot内置tomcat容器,使用jar包方式,打包后直接java -jar方式运行应用。
由于沟通过程出现问题,运维将应用构建成war包,部署到容器中并通过java -jar命令启动应用。应用正常启动并且功能正常,但是坑就在这步埋下了。
二、引发的问题
慢!就是最显著的现象。测试反馈切换到k8s环境后系统响应速度变慢很多,有时候页面刷新十几秒都加载不出来。通过日志分析可以看出接口响应比较慢都是出现在缓存过期,需要进行数据库IO操作的时候。
当接口请求过程出现数据库IO操作时,即使是简单的select一条记录,也要耗时3秒,即使是直接在IDE直接run起来的都没有这么慢。
![](https://i-blog.csdnimg.cn/blog_migrate/de46df806087f84abe99c1fcc3648196.png)
三、排查及解决方式
当时我们首先想到的可能有:1.数据库服务出现问题导致连接时间过长;2.容器中的网络环境问题导致数据库连接时间过长;3.容器内存过小导致频繁GC。这时候我们都还没关注到应用是通过java -jar app.war方式运行的!
排查后这三个可能性都被排除:
- 在容器中通过mysql客户端连接、执行sql语句没出现响应时间过长现象;
- 容器中ping数据库地址没有出现丢包或响应慢现象;
- 启用jvm参数-XX:+PrintGC,没有发现出现频繁GC或FULL GC。
排序以上三种可能后,我让运维把整个docker打包出来,我希望在本地能够重现出来。
果然在本地运行还是会出现接口响应很慢得情况,然后进去容器中查找日志,这是发现,里面run起来的竟然是个war包!应用是直接通过java -jar app.war方式运行。原来代码合并过程出错导致packaging方式还是原来的war,而应用打包、启动过程没出现报错,运维以为一切正常。
诧异的同时也很惊讶war包能直接java -jar运行起来(后文有答案),于是就把这个作为切入点,对比几种运行方式:
- docker + war:慢
- docker + jar:正常
- windows cmd + war:慢
- windows cmd + jar:正常
- ide + run(直接在ide启动):正常
可以看到问题出在war包这里。修改packaging值为jar后运维重新打包部署,接口响应速度就正常了。
所以不希望通过外置tomcat容器运行应用的情况下,建议jar打包方式,这也是springboot官方推荐的方式,才能保证应用以最正确的方式运作。
四、问题溯源
秉着归根结底的精神,不仅要消除问题现象,还要探究事情的本源。凭什么war包就会慢一点?
(一)Springboot应用常见打包方式:jar、war、pom
在pom文件的xsd文件中是这样描述packaging节点的: