软件环境:springboot2.1 + spring-data-jpa+ openjdk8
重启服务后,在用户第一次访问时,部分系统响应接口响应缓慢,有时候需要20多秒才返回,但第二次调度同一个接口则返回很快
网络上有该问题的三个解释说法:
1、dispatcherServlet 是懒加载的
2、数据库链接是懒加载的
3、linux 下真随机数生成器
在springboot的appcation.yml 中添加如下参数即可解决
spring.mvc.servlet.load-on-startup=1
在springboot的appcation.yml 中添加如下参数即可解决
#最小空闲连接数量(有待考证是否会初始化10个连接)
spring.datasource.hikari.minimum-idle: 10
或者在系统启动后进行预热:在@PostConstruct方案中执行一次空查询
此种方案意图是初始化一次查询以填充缓存(orm session、mysql、redis),加快下次同样请求返回结果的速度
我使用的是jpa,并且开启了spring.jpa.open-in-view=true,orm session是每次请求重置的;也没有开启redis,解决方案对我有限
熵池问题:https://blog.csdn.net/wysnxzm/article/details/98482143
在java启动时增加如下参数即可
-Djava.security.egd=file:/dev/./urandom
或者直接去修改$/jre/lib/security/java.security
文件也可
securerandom.source=file:/dev./urandom
以上处理方案执行 (当前预热方案还未执行)后,我的系统并未有改善。
基于该问题,我创建了一个AOP监测controller
的执行耗时,监测结果是controller
的返回几乎都在几十毫秒,与实际不符,
我然后又创建了一个拦截器监测从接到请求开始到返回的执行耗时,此处的监测结果与实际耗时比较接近,使用debug分析日志,看到有如下两种情况
controller
进行处理controller
返回后,间隔了很长时间(后台还运行了很多sql查询)才返回给拦截器从linux服务器中监测的日志截取如下
14:16:58.844 INFO c.y.s.monitor.interceptor.PerformanceInterceptor#20 : Interceptor performance: ip '192.168.1.109' request '/reception/receptionOfficial/page' start
14:16:58.844 DEBUG o.s.o.j.support.OpenEntityManagerInViewInterceptor#86 : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
14:16:58.850 DEBUG org.apache.catalina.loader.WebappClassLoaderBase#173 : findClass(java.lang.ObjectCustomizer)
14:17:06.412 DEBUG org.apache.catalina.loader.WebappClassLoaderBase#173 : --> Returning ClassNotFoundException
14:17:06.416 DEBUG org.apache.catalina.loader.WebappClassLoaderBase#173 : findClass(com.yzsoft.synergy.common.bean.BaseParamCustomizer)
14:17:07.923 DEBUG org.apache.catalina.loader.WebappClassLoaderBase#173 : --> Returning ClassNotFoundException
14:17:07.926 DEBUG org.apache.catalina.loader.WebappClassLoaderBase#173 : findClass(com.yzsoft.synergy.reception.bean.param.ReceptionActionParamCustomizer)
14:17:07.928 DEBUG org.apache.catalina.loader.WebappClassLoaderBase#173 : --> Returning ClassNotFoundException
14:17:07.931 DEBUG org.apache.catalina.loader.WebappClassLoaderBase#173 : findClass(com.yzsoft.synergy.reception.bean.param.ReceptionOfficialParamCustomizer)
14:17:07.933 DEBUG org.apache.catalina.loader.WebappClassLoaderBase#173 : --> Returning ClassNotFoundException
14:17:07.935 INFO c.yzsoft.synergy.monitor.aspect.PerformanceAspect#58 : controller performance: Start
? 看到第3行到第4行的时间差了吗,足足耗费了7.6s
ClassNotFound的几个Class肯定是在类定义中不存在的;
查看几个查找类的命名都存在*Customizer
后缀,排除这个后缀后,可以找到对应的类;
com.yzsoft.synergy.reception.bean.param.ReceptionOfficialParam
是controller
中接口方法的参数,该参数bean的继承关系如下:
`com.yzsoft.synergy.reception.bean.param.ReceptionOfficialParam`继承自
`com.yzsoft.synergy.reception.bean.param.ReceptionActionParam`继承自
`com.yzsoft.synergy.common.bean.BaseParam`继承自
`java.lang.Object`
分析ClassNotFound的几个Class,恰好是这几个类名+*Customizer
后缀
在测试过程中,使用了springboot 的不同版本(2.1x、2.2x、2.3x),openJDK的不同版本(8、11)均存在这个
findClass
过程在我本机(windows10+openjdk8)环境下,这个过程在几十毫秒内完成,不影响请求速度,在服务器(ubuntu18.4+openjdk8 )环境下,部分接口在这个过程中会花费几秒(如上日志)
目前该问题仍在进一步分析中:
1、为什么会查找*Customizer
类
2、为什么在linux中WebappClassLoaderBase的findClass会中断那么长时间
hibernate
懒加载的N+1 SQL
耗时 这实际上是hibernate
懒加载的N+1 SQL
问题,这个问题是老生常谈了。
主要解决途径如下:
通过DO-VO来转换封装取消懒加载属性
在返回前置空懒加载属性
通过json序列化过滤懒加载属性
需要重写接口的返回,否则你的restapi只能返回json字符串了
N+1 sql
变成 1+1 sql
)spring-data-jpa的默认实现并不能很好的支持第四种(需要重写dao的接口注解)
针对为什么在linux中WebappClassLoaderBase的findClass会中断那么长时间问题,我在jmc中开启飞行记录器l来跟踪下性能耗时
然后我发现,在受阻线程主要的时间跳跃点在java.beans.Introspector.getBeanInfo(Class, Class, int)
的调用
基于这个类在网络上搜索到:
https://www.jb51.cc/java/437748.html
以及他的调度类org.springframework.beans.CachedIntrospectionResults.getBeanInfo(Class)
的文章
https://blog.csdn.net/dennis_zane/article/details/83161898
分析下来,这个好像是tomcat的classLoader加载jar的老坑问题…
那么换思路:我是否可以换一个classLoader呢
在springboot中有以下两种思路:
打包成可执行的jar,不走tomcat的org.apache.catalina.loader.WebappClassLoaderBase
更换sevlet容器
我使用思路一试验了下,我的问题消失了,证明思路是对的,我卡卡…
我分析了4天,终于搞定了,神清气爽…
解决方案可以说就是改了一个字母,将pom.xml中
修改为
? 还有个问题 为什么会查找
*Customizer
类 下次再搞( 黑脸)