@Async 造成代码中用户信息与登录用户不一致的问题

场景:消息通知会发送给自己,微信公众号上的通知,提醒人与操作人不符合

思路:@Async 异步执行的时候,因为不在与主线程不是同一个线程所以,UserUtil.getCurrentUser()丢失当前用户信息,重新设置了用户上下文信息,以此为出发点排查。

 

1、首先在调用@Async方法的地方打上断点

 

 

 

2、在@Async执行的方法打上断点

 

3、设置 @Async 的核心线程池数量为1,这样保证多个请求执行的时候,@Async的线程都一样的

threadPoolTaskExecutor.setCorePoolSize(1);

@Configuration
public class TaskAsyncPoolConfig {
    /**
     * 异步线程池
     */
    @Bean
    public ThreadPoolTaskExecutor taskAsyncExecutor() {

        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(1);
        threadPoolTaskExecutor.setMaxPoolSize(100);
        threadPoolTaskExecutor.setQueueCapacity(20);
        threadPoolTaskExecutor.setThreadNamePrefix("async-task-");
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
}

 

4、debug模式下,添加参数查看

 

也可通过 Evaluate Expression 查看参数值

 

5、本地调试发起两次请求,用户ID不同

第一个请求:

用户id:9012827864423877687

@Async调用前线程:http-nio-8098-exec-4

@Async执行线程:async-task-1

UserUtil.getCurrentUser() == null

 

第二个请求:

用户id:1252476470807281738

@Async调用前线程:http-nio-8098-exec-2

@Async执行线程:async-task-1

UserUtil.getCurrentUser() 对应的用户id为 9012827864423877687

 

疑问?第一次请求UserUtil.getCurrentUser() == null,第二次请求UserUtil.getCurrentUser() != null

所以后面的每次请求因为@Async的核心线程池数设置为1,所以正常请求下,UserUtil.getCurrentUser() 都为第一次请求时候设置的那个用户信息

 

再来看下UserUtil.getCurrentUser() 是如何取值的

/**
 * 用户信息内容上下文
 * @author chongyang
 *
 */
public class UserContext {

    private static org.slf4j.Logger logger = LoggerFactory.getLogger(UserContext.class);

    /**用户信息*/
    private final static ThreadLocal<User> USER_CONTEXT = new ThreadLocal<User>();

    public static void setUser(User user) {
        USER_CONTEXT.remove();
        if (user != null) {
            logger.debug("设置当前用户信息:用户ID:{}", user.getId());
            USER_CONTEXT.set(user);
        }
    }

    /**
     * 获取用户信息
     * @return
     */
    public static User get() {
        return USER_CONTEXT.get();
    }

    /**
     * 清除用户信息
     */
    public static void remove() {
        USER_CONTEXT.remove();
    }

}

 

ThreadLocal 看来问题找到了

private final static ThreadLocal<User> USER_CONTEXT = new ThreadLocal<User>();

简单来说可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据。

如果不熟悉ThreadLocal ,可以看下面这篇文章

https://www.jianshu.com/p/3c5d7f09dfbd

 

如何解决?

@Async 执行完成后,在finally调用UserUtil.clearCurrentUser(); 清除ThreadLocal中设置的User

 

 

延申场景:@Async配置10个核心线程池,这样每次请求到不同的线程,

async-task-1 ~ async-task-10,每次线程第一个访问的用户ID不同,ThreadLocal的User即为第一次请求的用户,如果没有UserUtil.clearCurrentUser()清除当前ThreadLocal的User,会造成多次发起文件状态修改请求,微信消息通知的提醒人不一致的情况。也可能当前用户与线程中的设置的用户一致的情况,这种情况消息通知不会发送给自己,是正常的。

threadPoolTaskExecutor.setCorePoolSize(10);

@Configuration
public class TaskAsyncPoolConfig {
    /**
     * 异步线程池
     */
    @Bean
    public ThreadPoolTaskExecutor taskAsyncExecutor() {

        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(10);
        threadPoolTaskExecutor.setMaxPoolSize(100);
        threadPoolTaskExecutor.setQueueCapacity(20);
        threadPoolTaskExecutor.setThreadNamePrefix("async-task-");
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
}

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值