Spring session(redis存儲方式)監聽致使建立大量redisMessageListenerContailner-X線程redis
爲spring session添加springSessionRedisTaskExecutor線程池。spring
/** * 用於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):session
@Autowired( required = false //該處理監聽的線程池不是必須的,若是不自定義默認將使用SimpleAsyncTaskExecutor線程池 ) @Qualifier("springSessionRedisTaskExecutor") public void setRedisTaskExecutor(Executor redisTaskExecutor) { this.redisTaskExecutor = redisTaskExecutor; }
springSessionRedisTaskExecutor不是必須的,若是不自定義則spring默認將使用SimpleAsyncTaskExecutor線程池。併發
SimpleAsyncTaskExecutor:每次都將建立新的線程(說是「線程池」,其實並不是真正的池化,但它能夠設置最大併發線程數量。)
@EnableAsync開啓異步方法,背後默認使用的就是這個線程池。使用異步方法時若是業務場景存在頻繁的調用(該異步方法),請自定義線程池,以防止頻繁建立線程致使的性能消耗。若是該異步方法存在阻塞的狀況,又調用量大,注意有可能致使OOM(線程還未結束,又增長了更多的線程,最後致使內存溢出)。@Async註解能夠選擇使用自定義線程池。異步
說回RedisHttpSessionConfiguration,咱們接着看:async
@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線程。ui