• Spring session(redis存储方式)监听导致创建大量redisMessageListenerContailner-X线程


    待解决的问题

    Spring session(redis存储方式)监听导致创建大量redisMessageListenerContailner-X线程

    解决办法

    为spring session添加springSessionRedisTaskExecutor线程池。

    /**
     * 用于spring session,防止每次创建一个线程
     * @return
     */
    @Bean
    public ThreadPoolTaskExecutor springSessionRedisTaskExecutor(){
        ThreadPoolTaskExecutor springSessionRedisTaskExecutor = new ThreadPoolTaskExecutor();
        springSessionRedisTaskExecutor.setCorePoolSize(8);
        springSessionRedisTaskExecutor.setMaxPoolSize(16);
        springSessionRedisTaskExecutor.setKeepAliveSeconds(10);
        springSessionRedisTaskExecutor.setQueueCapacity(1000);
        springSessionRedisTaskExecutor.setThreadNamePrefix("Spring session redis executor thread: ");
        return springSessionRedisTaskExecutor;
    }
    

    原因

    在Spring Session(redis)的配置类源码中(RedisHttpSessionConfiguration):

    @Autowired(
        required = false    //该处理监听的线程池不是必须的,如果不自定义默认将使用SimpleAsyncTaskExecutor线程池
    )
    @Qualifier("springSessionRedisTaskExecutor")
    public void setRedisTaskExecutor(Executor redisTaskExecutor) {
        this.redisTaskExecutor = redisTaskExecutor;
    }
    

    springSessionRedisTaskExecutor不是必须的,如果不自定义则spring默认将使用SimpleAsyncTaskExecutor线程池。

    题外话

    SimpleAsyncTaskExecutor:每次都将创建新的线程(说是“线程池”,其实并非真正的池化,但它可以设置最大并发线程数量。)
    @EnableAsync开启异步方法,背后默认使用的就是这个线程池。使用异步方法时如果业务场景存在频繁的调用(该异步方法),请自定义线程池,以防止频繁创建线程导致的性能消耗。如果该异步方法存在阻塞的情况,又调用量大,注意有可能导致OOM(线程还未结束,又增加了更多的线程,最后导致内存溢出)。@Async注解可以选择使用自定义线程池。

    它创建了SimpleAsyncTaskExecutor

    说回RedisHttpSessionConfiguration,我们接着看:

    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer() {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(this.redisConnectionFactory);
        if (this.redisTaskExecutor != null) {
            container.setTaskExecutor(this.redisTaskExecutor);
        }
    
        if (this.redisSubscriptionExecutor != null) {
            container.setSubscriptionExecutor(this.redisSubscriptionExecutor);
        }
    
        container.addMessageListener(this.sessionRepository(), Arrays.asList(new PatternTopic("__keyevent@*:del"), new PatternTopic("__keyevent@*:expired")));
        container.addMessageListener(this.sessionRepository(), Collections.singletonList(new PatternTopic(this.sessionRepository().getSessionCreatedChannelPrefix() + "*")));
        return container;
    }
    

    RedisMessageListenerContainer正是处理监听的类,RedisMessageListenerContainer设置了不为空的redisTaskExecutor,因为spring session默认没有配置该Executor,那RedisMessageListenerContainer在处理监听时怎么使用线程呢?我们接着看RedisMessageListenerContainer的源码:

    public void afterPropertiesSet() {
        if (this.taskExecutor == null) {
            this.manageExecutor = true;
            this.taskExecutor = this.createDefaultTaskExecutor();
        }
    
        if (this.subscriptionExecutor == null) {
            this.subscriptionExecutor = this.taskExecutor;
        }
    
        this.initialized = true;
    }
    
    protected TaskExecutor createDefaultTaskExecutor() {
        String threadNamePrefix = this.beanName != null ? this.beanName + "-" : DEFAULT_THREAD_NAME_PREFIX;
        return new SimpleAsyncTaskExecutor(threadNamePrefix);
    }
    

    afterPropertiesSet()这个方法熟悉吧,这个方法将在所有的属性被初始化后调用(InitializingBean接口细节这里不再赘述)。
    所以如果用户没有定义springSessionRedisTaskExecutor,Spring session将调用createDefaultTaskExecutor()方法创建SimpleAsyncTaskExecutor线程池。而这个“线程池”处理任务时每次都创建新的线程。所以你会发现很多个redisMessageListenerContailner-X线程。

  • 相关阅读:
    Linux基操:软件安装的方式:解压缩安装
    Linux基操:linux命令失效解决方法
    Linux基操:软件安装的方式:rpm,&&环境变量配置
    Linux基操:进程管理
    Linux基操:磁盘管理,扩展文件挂载问题
    Linux基操(Centos7):用户管理
    Linux基操:文件内容查看方式&&软链接硬链接(拓展)
    Linux基操:目录相关命令
    多测师肖sir_高级金牌讲师__面试和答案归纳
    多测师肖sir__高级金牌讲师 ___python中len函数
  • 原文地址:https://www.cnblogs.com/aoeiuv/p/9565617.html
Copyright © 2020-2023  润新知