项目上有一个保存PNG图片并展示的功能失效了,以前这个功能是没问题的,现在出现了问题,下面记录一下问题的排查过程。
问题表征
我本地的开发环境是Windows10,IDEA2020,本地启动功能是正常的,编辑流程可以正常展示流程的PNG格式的图片,如下图所示
![](https://img.haomeiwen.com/i14026161/eaace3d49eed7a5c.png)
然后应用打包部署到centos环境下,进行功能测试,图片无法正常展示,如下图所示
![](https://img.haomeiwen.com/i14026161/f51cf0433607bfdc.png)
打开chrome控制台,有1个图片请求报错406
![](https://img.haomeiwen.com/i14026161/aff58ecdacf38f8b.png)
根据这个请求定位到后台代码,由于是部署在Linux环境下的,且根据日志无法定位问题,所以我们采用idea远程debug的方法进行调试。
问题排查
在查找问题的过程中,win10本地调试和centos功能测试,本地开发环境是正常的,centos上功能是不正常的。
本地环境和centos环境的zk、es、db都是用的同一套,排除依赖的第三方组件异常的问题。
定位到保存流程的代码进行远程debug,是进行PNG转码处理的
![](https://img.haomeiwen.com/i14026161/aab3c019012ca6f2.png)
执行这行代码,抛出了一个异常
![](https://img.haomeiwen.com/i14026161/d56aba5efa05766b.png)
这个异常是类加载抛出的异常,类初始化失败
Could not initialize class org.apache.batik.gvt.font.FontFamilyResolver
这个是用的apache的batik包来处理的,有点老,是batik-1.7版本的,bytecode version :45.3(Java 1.1)。
我一开始怀疑可能是包版本太老了,后来换成了batik-1.11,结果还是无法展示图片,和包版本无关。
后来经过搜索,Could not initialize
可能是静态代码块或者是静态变量加载失败导致的。
静态变量和静态代码块只在类加载的时候执行一次,且只会执行一次。后续抛出异常的时候有可能会覆盖这个错误,导致排查起来被误导。
重启应用程序(tomcat),定位到类加载失败的static代码块中,一步步执行。
static代码块中有一处执行抛出了异常
java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManager
这个异常和设备的显示有关,Linux作为服务器,相比于个人PC,会缺少显示设备、鼠标、键盘等。
解决方案是设置jvm启动参数,模拟输入设备特性
-Djava.awt.headless=true
设置这个参数之后,还是会有报错,报错如下:
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.151-1.b12.el6_9.x86_64/jre/lib/amd64/libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory
猜测是openjdk缺少某些函数库,这个是容器自带的opnejdk,后来将openjdk换成oracle jdk8u181,可以正常展示图片了。
小结
首先根据页面上的错误定位到后端代码,进行排查。
其次由于是部署在Linux上,且没有清晰、直观的日志可供排查,所以选择了远程debug的方法,在本地进行调试。
调试过程中执行语句得到了Could not initialize class
,这一步怀疑错方向了,这个错其实是类加载的问题,在加载静态变量或者静态代码块的时候,如果抛出异常,就会导致类加载失败。
还有1个问题是静态代码块只会在类加载的时候加载一次,加载完成后面都不会再加载了,所以对问题排查带来了一些困难。
最终问题的解决方案是加一个启动项:-Djava.awt.headless=true
,另外还需要将openjdk更换为oracle jdk,openjdk缺少一些必要的库,导致产生了异常。
参考文章:
https://www.cnblogs.com/helf/p/12012619.html
https://blog.csdn.net/chen2526264/article/details/80534239
网友评论