一、背景
问题: 在前公司遇到OOM:unable to create new native thread
的相关问题(1.5.6.Release
的古董版本),经排查是@Async 造成
现象: 控制台日志可以发现线程名称 SimpleAsyncTaskExecutor-999
中 线程ID一直递增 , 所以怀疑是 没有使用线程池 或者 线程池参数设置不合理,后续排查发现是@Async
造成的,对此进行了排查并在团队内部做了故障排查的分享。
这两天前同事联系要帮忙解决问题,说在新公司接手老项目遇到这问题,只隐约记得之前有分享过,内容不记得了,要帮忙分析一下,写一篇分析记录下
二、使用的正确姿势
三、源码分析
主要是 OOM不能创建线程 问题引起,所以源码以排查OOM为主要目的,重点关注 线程 或者 线程池的创建和使用。
入门思路:既然@EnableAsync
注解开启异步,那点进去看看@EnableAsync
里面到底处理了啥,然后在一步一步跟就行了。
1、@EnableAsync
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 此处 引入了 AsyncConfigurationSelector.class
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
......
/**
* Indicate how async advice should be applied.
* <p><b>The default is {@link AdviceMode#PROXY}.</b>
* Please note that proxy mode allows for interception of calls through the proxy
* only. Local calls within the same class cannot get intercepted that way; an
* {@link Async} annotation on such a method within a local call will be ignored
* since Spring's interceptor does not even kick in for such a runtime scenario.
* For a more advanced mode of interception, consider switching this to
* {@link AdviceMode#ASPECTJ}.
*/
AdviceMode mode() default AdviceMode.PROXY;
......
}
如上EnableAsync
里面就一堆属性,大概看下都有些啥有个大体印象,后面用到的时候回来查。
稍微注意一个AdviceMode
,下一步就会用到,特别注意下其注释,翻译如下:
指示应如何应用异步建议。
默认值为AdviceMode.PROXY 。
请注意,代理模式只允许通过代理拦截呼叫。
同一类中的本地调用不能以这种方式被拦截;
本地调用中此类方法上的Async注释将被忽略,因为 Spring 的拦截器甚至不会在此类运行时场景中启动。
对于更高级的拦截模式,请考虑将其切换为AdviceMode.ASPECTJ 。
@Import(AsyncConfigurationSelector.class)
,@Import
啥意思 先不用深究,暂时知道springboot启动时会加载@Import
指定的类就可以了。
点击去看下AsyncConfigurationSelector.class
写了啥
2、AsyncConfigurationSelector.class
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
......
@Override
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
// 默认 AdviceMode mode() default AdviceMode.PROXY;
case PROXY:
return new String[] {
ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[] {
ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
逻辑很简单,按类型选择导入配置,因为@EnableAsync
中默认 AdviceMode.PROXY,直接点进ProxyAsyncConfiguration
看怎么处理
3、ProxyAsyncConfiguration
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
// 后置处理器
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
bpp.configure(this.executor, this.exceptionHandler);
Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
return bpp;
}
}
创建了一个AsyncAnnotationBeanPostProcessor后置处理器对象,后续代码都是对此处理器的属性设置。
继承了 AbstractAsyncConfiguration
,一般Configuration都是比较重要了,点击去看一眼
@Configuration
public abstract class AbstractAsyncConfiguration implements ImportAware {
@Nullable
protected AnnotationAttributes enableAsync;
@Nullable
protected Executor executor;
@Nullable
protected AsyncUncaughtExceptionHandler exceptionHandler;
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableAsync = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
if (this.enableAsync == null) {
throw new IllegalArgumentException(
"@EnableAsync is not present on importing class " + importMetadata.getClassName());
}
}
/**
* Collect any {@link AsyncConfigurer} beans through autowiring.
*/
// 此处加载了 AsyncConfigurer 配置
@Autowired(required = false)
void setConfigurers(Collection<AsyncConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
if (configurers.size() > 1) {
throw new IllegalStateException("Only one AsyncConfigurer may exist");
}
// 调用了上述正确使用中 方案二, 实现AsyncConfigurer接口的配置
AsyncConfigurer configurer = configurers.iterator().next();
this.executor = configurer.getAsyncExecutor();
this.exceptionHandler = configurer.getAsyncUncaughtExceptionHandler();
}
}
可以看到,setConfigurers 方法中加载了AsyncConfigurer
配置,二、使用的正确姿势
中提到了配置线程池方案二
,就是在此时被调用加载的,如果配置了,此时 executor 已经加载了。
继续看,直接看AsyncAnnotationBeanPostProcessor怎么处理的
4、AsyncAnnotationBeanPostProcessor
public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
@Nullable
private Supplier<Executor> executor;
public AsyncAnnotationBeanPostProcessor() {
setBeforeExistingAdvisors(true);
}
...... 忽略其他 ......
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
// 创建AsyncAnnotationAdvisor,且传入了 executor
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
if (this.asyncAnnotationType != null) {
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
}
advisor.setBeanFactory(beanFactory);
this.advisor = advisor;
}
}
看 @Override setBeanFactory
,里面创建了一个AsyncAnnotationAdvisor
对象,且参数为我们要重点关注的线程池对象: executor ,点击去看看
5、AsyncAnnotationAdvisor
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
...... 忽略其他 ......
/**
* Create a new {@code AsyncAnnotationAdvisor} for the given task executor.
* @param executor the task executor to use for asynchronous methods
* (can be {@code null} to trigger default executor resolution)
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use to
* handle unexpected exception thrown by asynchronous method executions
* @since 5.1
* @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
*/
@SuppressWarnings("unchecked")
public AsyncAnnotationAdvisor(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
asyncAnnotationTypes.add(Async.class);
try {
asyncAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
// If EJB 3.1 API not present, simply ignore.
}
// 此处构建 buildAdvice 时用到的executor,要点进去看看方法, 如下
this.advice = buildAdvice(executor, exceptionHandler);
this.pointcut = buildPointcut(asyncAnnotationTypes);
}
...... 忽略其他 ......
protected Advice buildAdvice(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
// 创建了一个拦截器,传了个null进去,要看看拦截器处理了什么
AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
// 拦截器中设置了 executor
interceptor.configure(executor, exceptionHandler);
return interceptor;
}
...... 忽略其他 ......
}
该构造器主要两个方法,看名字就知道是 AOP 的东西:
this.advice = buildAdvice(executor, exceptionHandler);
this.pointcut = buildPointcut(asyncAnnotationTypes);
切点是定义什么时候触发aop执行advice,先不用管。
重点看 advice ,这才是处理逻辑,创建了一个 拦截器 AnnotationAsyncExecutionInterceptor
,点进去看看 拦截器中做了什么
6、AnnotationAsyncExecutionInterceptor
public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionInterceptor {
/**
* Create a new {@code AnnotationAsyncExecutionInterceptor} with the given executor
* and a simple {@link AsyncUncaughtExceptionHandler}.
* @param defaultExecutor the executor to be used by default if no more specific
* executor has been qualified at the method level using {@link Async#value()};
* as of 4.2.6, a local executor for this interceptor will be built otherwise
*/
// 直接点进去查看父类
public AnnotationAsyncExecutionInterceptor(@Nullable Executor defaultExecutor) {
super(defaultExecutor);
}
...... 忽略其他