解決Spring session(redis存儲(chǔ)方式)監(jiān)聽導(dǎo)致創(chuàng)建大量redisMessageListenerContailner-X線程問題
待解決的問題
Spring session(redis存儲(chǔ)方式)監(jiān)聽導(dǎo)致創(chuàng)建大量redisMessageListenerContailner-X線程
解決辦法
為spring session添加springSessionRedisTaskExecutor線程池。
/** * 用于spring session,防止每次創(chuàng)建一個(gè)線程 * @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 //該處理監(jiān)聽的線程池不是必須的,如果不自定義默認(rèn)將使用SimpleAsyncTaskExecutor線程池 ) @Qualifier("springSessionRedisTaskExecutor") public void setRedisTaskExecutor(Executor redisTaskExecutor) { this.redisTaskExecutor = redisTaskExecutor; }
springSessionRedisTaskExecutor不是必須的,如果不自定義則spring默認(rèn)將使用SimpleAsyncTaskExecutor線程池。
題外話
SimpleAsyncTaskExecutor:每次都將創(chuàng)建新的線程(說是“線程池”,其實(shí)并非真正的池化,但它可以設(shè)置最大并發(fā)線程數(shù)量。)
@EnableAsync開啟異步方法,背后默認(rèn)使用的就是這個(gè)線程池。使用異步方法時(shí)如果業(yè)務(wù)場景存在頻繁的調(diào)用(該異步方法),請自定義線程池,以防止頻繁創(chuàng)建線程導(dǎo)致的性能消耗。如果該異步方法存在阻塞的情況,又調(diào)用量大,注意有可能導(dǎo)致OOM(線程還未結(jié)束,又增加了更多的線程,最后導(dǎo)致內(nèi)存溢出)。@Async注解可以選擇使用自定義線程池。
它創(chuàng)建了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正是處理監(jiān)聽的類,RedisMessageListenerContainer設(shè)置了不為空的redisTaskExecutor,因?yàn)閟pring session默認(rèn)沒有配置該Executor,那RedisMessageListenerContainer在處理監(jiān)聽時(shí)怎么使用線程呢?我們接著看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()這個(gè)方法熟悉吧,這個(gè)方法將在所有的屬性被初始化后調(diào)用(InitializingBean接口細(xì)節(jié)這里不再贅述)。
所以如果用戶沒有定義springSessionRedisTaskExecutor,Spring session將調(diào)用createDefaultTaskExecutor()方法創(chuàng)建SimpleAsyncTaskExecutor線程池。而這個(gè)“線程池”處理任務(wù)時(shí)每次都創(chuàng)建新的線程。所以你會(huì)發(fā)現(xiàn)很多個(gè)redisMessageListenerContailner-X線程。
總結(jié)
以上所述是小編給大家介紹的解決Spring session(redis存儲(chǔ)方式)監(jiān)聽導(dǎo)致創(chuàng)建大量redisMessageListenerContailner-X線程問題,希望對(duì)大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
詳解Java如何實(shí)現(xiàn)數(shù)值校驗(yàn)的算法
給定一個(gè)字符串如何判斷它是否為數(shù)值類型?本文將帶著大家學(xué)習(xí)一下如何利用Java實(shí)現(xiàn)這個(gè)判斷算法,感興趣的小伙伴可以學(xué)習(xí)一下2022-04-04深入淺出講解Spring框架中依賴注入與控制反轉(zhuǎn)及應(yīng)用
依賴注入(Dependency?Injection)和控制反轉(zhuǎn)(Inversion?of?Control)是同一個(gè)概念。具體含義是:當(dāng)某個(gè)角色(可能是一個(gè)Java實(shí)例,調(diào)用者)需要另一個(gè)角色(另一個(gè)Java實(shí)例,被調(diào)用者)的協(xié)助時(shí),在?傳統(tǒng)的程序設(shè)計(jì)過程中,通常由調(diào)用者來創(chuàng)建被調(diào)用者的實(shí)例2022-03-03Java TimedCache 帶時(shí)間緩存工具類詳解使用
工具類是包含集合框架、遺留的 collection 類、事件模型、日期和時(shí)間設(shè)施、國際化和各種實(shí)用工具類(字符串標(biāo)記生成器、隨機(jī)數(shù)生成器和位數(shù)組、日期Date類、堆棧Stack類、向量Vector類等)。集合類、時(shí)間處理模式、日期工具等各類常用工具包,本文將介紹帶時(shí)間緩存工具類2021-10-10如何把spring boot應(yīng)用發(fā)布到Harbor
這篇文章主要介紹了如何把spring boot應(yīng)用發(fā)布到Harbor,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11關(guān)于spring循環(huán)依賴問題及解決方案
這篇文章主要介紹了關(guān)于spring循環(huán)依賴問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06Java中json格式化BigDecimal保留2位小數(shù)
這篇文章主要給大家介紹了關(guān)于Java中json格式化BigDecimal保留2位小數(shù)的相關(guān)資料,BigDecimal是Java中的一個(gè)數(shù)學(xué)庫,可以實(shí)現(xiàn)高精度計(jì)算,文中給出了詳細(xì)的代碼實(shí)例,需要的朋友可以參考下2023-09-09java中List<對(duì)象>如何根據(jù)對(duì)象的一個(gè)屬性進(jìn)行去重
這篇文章主要給大家介紹了關(guān)于java中List<對(duì)象>如何根據(jù)對(duì)象的一個(gè)屬性進(jìn)行去重的相關(guān)資料,在開發(fā)中可能會(huì)遇到很多需要去重的情況,比如Person對(duì)象有name跟age兩個(gè)屬性,需要根據(jù)age進(jìn)行去重,需要的朋友可以參考下2023-08-08