TransmittableThreadLocal 和 @Async注解一起使用的问题

**业务背景:**在sass(租户)系统中,需要传递租户信息(tenant_id) 使用TransmittableThreadLocal传递租户信息。
**问题:**使用了@Async租户传递(tennat_id) 出现的问题,和用户的tenant_id对不上。

排查问题需要的前置知识(别人的博客写的好,直接引用)

1.sass系统实现方案:https://blog.csdn.net/johntsu2006/article/details/100591615
2.ThreadLocal的使用:https://www.cnblogs.com/hama1993/p/10382523.html
3.InheritableThreadLocal的使用:https://www.cnblogs.com/hama1993/p/10400265.html
4.TransmittableThreadLocal:https://www.cnblogs.com/hama1993/p/10409740.html

说明:在此直接引用他人的博客,这几篇博客帮助我解决了问题,在此提出感谢。

正文

1.首先问题出现了@Async注解上的方法上,这里我们业务上没有对@Async线程池进行配置,觉得默认线程池SimpleAsyncTaskExecutor(来一个任务创建一个线程,不会复现线程)。
2.我通过log.info() @Async 方法中的线程ID发现是线程是复用的,那说明不是采用SimpleAsyncTaskExecutor。
3.查看说明 @EnableAsync注释文档
在这里插入图片描述
发现其实没有另外配置并不是优先使用SimpleAsyncTaskExecutor 线程池,先去看spring 是否存在 org.springframework.core.task.TaskExecutor bean 或者 名称为"taskExecutor"线程池。找不到才默认使用SimpleAsyncTaskExecutor。我才看自己业务当中,引入jar宝就存在org.springframework.core.task.TaskExecutor bean。
4.说明SimpleAsyncTaskExecutor并不是很好的解决方案,对线程不进行复用,对系统的性能是一种浪费,还有可能造成OOM。
5.解决方案:自定义@Async线程池使用TtlExecutors包装

@Slf4j
public class AsyncExecutorConfig implements AsyncConfigurer {

  @Override
  public Executor getAsyncExecutor() {
      ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
	  // 配置核心线程数
	  executor.setCorePoolSize(10);
	  // 配置最大线程数
	  executor.setMaxPoolSize(50);
	  // 配置队列大小
	  executor.setQueueCapacity(99999);
     //配置线程池中的线程的名称前缀
      executor.setThreadNamePrefix("async-service-");

       // rejection-policy:当pool已经达到max size的时候,如何处理新任务
       // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
	  executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
      //执行初始化
      executor.initialize();
      //TtlExecutors使用这个包装,这个在前置知识第四点的博客中可以明白其原理
    return TtlExecutors.getTtlExecutorService(executor.getThreadPoolExecutor());
  }

}

6.找到配置的@Async配置的线程池,springboot 中的spring-boot-autoconfigure包会自动配置的一个@Async线程池。源代码如下:

@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {

	/**
	 * Bean name of the application {@link TaskExecutor}.
	 */
	public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";

	@Bean
	@ConditionalOnMissingBean
	public TaskExecutorBuilder taskExecutorBuilder(TaskExecutionProperties properties,
			ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,
			ObjectProvider<TaskDecorator> taskDecorator) {
		TaskExecutionProperties.Pool pool = properties.getPool();
		TaskExecutorBuilder builder = new TaskExecutorBuilder();
		builder = builder.queueCapacity(pool.getQueueCapacity());
		builder = builder.corePoolSize(pool.getCoreSize());
		builder = builder.maxPoolSize(pool.getMaxSize());
		builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
		builder = builder.keepAlive(pool.getKeepAlive());
		Shutdown shutdown = properties.getShutdown();
		builder = builder.awaitTermination(shutdown.isAwaitTermination());
		builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
		builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
		builder = builder.customizers(taskExecutorCustomizers.orderedStream()::iterator);
		builder = builder.taskDecorator(taskDecorator.getIfUnique());
		return builder;
	}

	@Lazy
	@Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
			AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
	@ConditionalOnMissingBean(Executor.class)
	public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
		return builder.build();
	}

}

而ThreadPoolTaskExecutor的继承于AsyncListenableTaskExecutor,AsyncListenableTaskExecutor继承了AsyncTaskExecutor,AsyncTaskExecutor继承TaskExecutor。
@EnableAsync 注释文档说明:
By default, Spring will be searching for an associated thread pool definition: either a unique org.springframework.core.task.TaskExecutor bean in the context, or an java.util.concurrent.Executor bean named “taskExecutor” otherwise. If neither of the two is resolvable, a org.springframework.core.task.SimpleAsyncTaskExecutor will be used to process async method invocations. Besides, annotated methods having a void return type cannot transmit any exception back to the caller. By default, such uncaught exceptions are only logged.

总结:出现这个问题是我对@Async和TransmittableThreadLocal 了解很少情况下盲目使用在项目中,才踩到坑的。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值