现象:
测试对平台接口进行压测,平均每个接口TPS为700多,无法继续压上去。
排查:
1.服务争抢资源
227启着十几个java进程,还有中间件,mysql,python。如果把这些服务都关掉的话,上1000肯定是没有问题的。
通过查看:每秒上下文切换次数 四万多
2.查看项目线程状态
让测试重新开始压测,通过arthas命令查看当前项目占用线程状态(thread)
可以看到大部分进程进入BLOCKED状态,进入tomcat线程查看堆栈信息。
可以看出具体阻塞的原因是tomcatEmbeddedClassLoader
3.查看出现锁的具体源码
投机取巧,先对loadClass进行watch操作,查看详细的出入参数
[arthas@31524]$ watch org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader loadClass '{params,returnObj,throwExp}' -n 5 -x 3 ts=2022-09-13 15:47:38; [cost=2.3656858288008095E10ms] result=@ArrayList[ts=2022-09-13 15:47:38; [cost=2.3656858286638813E10ms] result=@ArrayList[ts=2022-09-13 15:47:38; [cost=2.3656858287444527E10ms] result=@ArrayList[ts=2022-09-13 15:47:38; [cost=2.3656858286489414E10ms] result=@ArrayList[ @Object[][ @String[org.springframework.cloud.context.environment.EnvironmentChangeEvent], @Boolean[false], ], null, java.lang.ClassNotFoundException: org.springframework.cloud.context.environment.EnvironmentChangeEvent at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader.loadClass(TomcatEmbeddedWebappClassLoader.java:68) at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1188) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:348) at org.springframework.util.ClassUtils.forName(ClassUtils.java:280) @Object[][ at com.ulisesbocchio.jasyptspringboot.caching.RefreshScopeRefreshedEventListener.isAssignable(RefreshScopeRefreshedEventListener.java:45) @String[org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent], at com.ulisesbocchio.jasyptspringboot.caching.RefreshScopeRefreshedEventListener.onApplicationEvent(RefreshScopeRefreshedEventListener.java:31) at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) @Boolean[false], ], null,at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) java.lang.ClassNotFoundException: org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEventat org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader.loadClass(TomcatEmbeddedWebappClassLoader.java:68) at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1188) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:348) at org.springframework.util.ClassUtils.forName(ClassUtils.java:280) ts=2022-09-13 15:47:38; [cost=2.3656858287126102E10ms] result=@ArrayList[at com.ulisesbocchio.jasyptspringboot.caching.RefreshScopeRefreshedEventListener.isAssignable(RefreshScopeRefreshedEventListener.java:45)
可以看出参数为org.springframework.cloud.context.environment.EnvironmentChangeEvent。
并且抛出的异常为找不到org.springframework.cloud.context.environment.EnvironmentChangeEvent此类。
回归死锁堆栈信息
回顾tomcat实现的原理
tomcat对请求到的请求会进行封装
doPost->封装成event->由指定event监听进行处理此请求。
在详细的信息中可以看到ApplicationListener由RefreshScopeRefreshedEventListener实现,并且其中isAssignable出现了死锁。
其中的className为org.springframework.cloud.context.environment.EnvironmentChangeEvent。
也就是watch报错的参数。
并且可以看到这个包并非tomcat自己实现的包,而是第三方包com.ulisesbocchio.jasyptspringboot.caching
4.排查出错的包
查看包的详细
通过pom寻找,查到我们导入了他的依赖
并且进行了stater注入
解决
问题一:org.springframework.cloud.context.environment.EnvironmentChangeEvent 类
BAS平台项目不是分布式服务,没有此类,所以这就是导致死锁报错的原因。
是否需要引入cloud组件来解决此问题
问题二:是否可以修改此依赖来解决
当前版本为3.0.3
com.github.ulisesbocchio
jasypt-spring-boot
${jasypt.version}
目前去除了stater版本,改为此依赖。
打包部署后,测试重新压测。TPS达到了8K多。
但是修改此依赖导致其他配置要修改(如密码明文配置等)还需要重新适配
问题三:是否已经出现版本适配此bug
通过查询
官方已经发现此问题,升级到3.0.4即可