待解决的问题

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线程。

最新文章

  1. JSON(及其在ajax前后端交互的过程)小识
  2. java提高篇(三十)-----Iterator
  3. double截取小数点位数
  4. html meta中的viewport指令
  5. Win10 FaceAPI小demo开发问题汇总
  6. WIN8 浏览器排版不兼容问题
  7. BZOJ-1861 Book 书架 Splay
  8. MFC中使用Duilib--2
  9. MySQL Date 函数
  10. 开发移动端web的一些知识
  11. mybatis------xml的一些规范等
  12. JavaScript一个鼠标滚动事件的实例
  13. Win10无法使用小娜搜索本地应用问题的解决方案
  14. EasyPR源码剖析(4):车牌定位之Sobel算子定位
  15. ftp协议 主动和被动两种模式模式
  16. [CodeForces - 447B] B - DZY Loves Strings
  17. OAF_OAF控件系列7 - Tree的实现(案列)
  18. Java类中的各种成员的加载顺序
  19. pageadmin CMS Sql新建数据库和用户名教程
  20. redis 选择数据库

热门文章

  1. JMeter性能测试基础 (4)-使用JMeter录制测试脚本
  2. Activiti动态设置办理人扩展
  3. Linux预处理、编译、汇编、链接和运行的过程(包括一些基本的命令)
  4. MySQL Binlog详解
  5. pgm15
  6. BZOJ4372 烁烁的游戏(动态点分治+线段树)
  7. AC自动机-HDU2222-模板题
  8. Leapin' Lizards HDU - 2732 (恶心的建图。。)
  9. 洛谷 P2596 [ZJOI2006]书架 解题报告
  10. poj2386(简单的dfs/bfs)