问题描述:
项目测试中,未发现问题,后面压测同学进行压测发现程序只执行部分交易,后面的交易都卡在第一步了,CPU、内存、GC都正常。
问题定位:
通过arthas定位发现线程卡在((Spring 5.1.16版本)AsyncExecutionInterceptor 117行,最终是FutureTask的get()方法)。
问题原因:
代码中同时使用了PROXY和ASPECTJ两种代理,先走PROXY代理(在拦截器中走进了异步),后面进入到真正的业务方法,然后走进了ASPECTJ代理(这是编译时代理),这里也会获取线程池中的线程
解决方案:
- 去除Future返回值,前提是程序不需要返回值的情况
- 去除PROXY、ASPECTJ代理中,其中一个代理
- @EnableAsync中的mode,建议统一用一种
注意事项
- 程序中使用了ASPECTJ代理,但是没有让spring容器去管理,导致AnnotationAsyncExecutionAspect的beanFactory为空,此时会有两种情况:
- Async(有值),会抛异常
- Async(无值),此时没有拿到线程池,不会开启新的线程,会在当前线程中继续执行
代码执行流程
- 启动项目
2.程序启动成功,发起http请求
通过堆栈信息可以看出程序首先进入了jdk动态代理类
这里开启了异步
进入到业务方法
可以发现此时业务方法已经被代理(AspectJ静态代理)
会调用下面的方法
这里会开启异步
最终在最后一行代码上打上端点,查看线程,可以发现一次请求占用两个线程
结论:当交易密集的时候,线程来不及释放,最终导致线程耗尽。