午夜剧场伦理_日本一道高清_国产又黄又硬_91黄色网战_女同久久另类69精品国产_妹妹的朋友在线

您的位置:首頁技術(shù)文章
文章詳情頁

springboot中@Async默認(rèn)線程池導(dǎo)致OOM問題

瀏覽:15日期:2023-05-15 10:07:27

前言:

1.最近項(xiàng)目上在測(cè)試人員壓測(cè)過程中發(fā)現(xiàn)了OOM問題,項(xiàng)目使用springboot搭建項(xiàng)目工程,通過查看日志中包含信息:unable to create new native thread

內(nèi)存溢出的三種類型:1.第一種OutOfMemoryError: PermGen space,發(fā)生這種問題的原意是程序中使用了大量的jar或class2.第二種OutOfMemoryError: Java heap space,發(fā)生這種問題的原因是java虛擬機(jī)創(chuàng)建的對(duì)象太多3.第三種OutOfMemoryError:unable to create new native thread,創(chuàng)建線程數(shù)量太多,占用內(nèi)存過大

初步分析:

1.初步懷疑是線程創(chuàng)建太多導(dǎo)致,使用jstack 線程號(hào) > /tmp/oom.log將應(yīng)用的線程信息打印出來。查看oom.log,發(fā)現(xiàn)大量線程處于Runnable狀態(tài),基本可以確認(rèn)是線程創(chuàng)建太多了。

代碼分析:

1.出問題的微服務(wù)是日志寫庫服務(wù),對(duì)比日志,鎖定在writeLog方法上,wirteLog方法使用spring-@Async注解,寫庫操作采用的是異步寫入方式。2.之前沒有對(duì)@Async注解深入研究過,只是知道可以自定義內(nèi)部線程池,經(jīng)查看,日志寫庫服務(wù)并未自定義異步配置,使用的是spring-@Async默認(rèn)異步配置3.首先簡單百度了下,網(wǎng)上提到@Async默認(rèn)異步配置使用的是SimpleAsyncTaskExecutor,該線程池默認(rèn)來一個(gè)任務(wù)創(chuàng)建一個(gè)線程,在壓測(cè)情況下,會(huì)有大量寫庫請(qǐng)求進(jìn)入日志寫庫服務(wù),這時(shí)就會(huì)不斷創(chuàng)建大量線程,極有可能壓爆服務(wù)器內(nèi)存。

借此機(jī)會(huì)也學(xué)習(xí)了下SimpleAsyncTaskExecutor源碼,總結(jié)如下:

1.SimpleAsyncTaskExecutor提供了限流機(jī)制,通過concurrencyLimit屬性來控制開關(guān),當(dāng)concurrencyLimit>=0時(shí)開啟限流機(jī)制,默認(rèn)關(guān)閉限流機(jī)制即concurrencyLimit=-1,當(dāng)關(guān)閉情況下,會(huì)不斷創(chuàng)建新的線程來處理任務(wù),核心代碼如下:

public void execute(Runnable task, long startTimeout) { Assert.notNull(task, 'Runnable must not be null'); Runnable taskToUse = (this.taskDecorator != null ? this.taskDecorator.decorate(task) : task); //判斷是否開啟限流機(jī)制 if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) { //執(zhí)行前置操作,進(jìn)行限流 this.concurrencyThrottle.beforeAccess(); //執(zhí)行完線程任務(wù),會(huì)執(zhí)行后置操作concurrencyThrottle.afterAccess(),配合進(jìn)行限流 doExecute(new ConcurrencyThrottlingRunnable(taskToUse)); } else { doExecute(taskToUse); }}

2.SimpleAsyncTaskExecutor限流實(shí)現(xiàn)

首先任務(wù)進(jìn)來,會(huì)循環(huán)判斷當(dāng)前執(zhí)行線程數(shù)是否超過concurrencyLimit,如果超了,則當(dāng)前線程調(diào)用wait方法,釋放monitor對(duì)象鎖,進(jìn)入等待

protected void beforeAccess() {if (this.concurrencyLimit == NO_CONCURRENCY) {throw new IllegalStateException('Currently no invocations allowed - concurrency limit set to NO_CONCURRENCY');}if (this.concurrencyLimit > 0) {boolean debug = logger.isDebugEnabled();synchronized (this.monitor) {boolean interrupted = false;while (this.concurrencyCount >= this.concurrencyLimit) {if (interrupted) {throw new IllegalStateException('Thread was interrupted while waiting for invocation access, ' +'but concurrency limit still does not allow for entering');}if (debug) {logger.debug('Concurrency count ' + this.concurrencyCount +' has reached limit ' + this.concurrencyLimit + ' - blocking');}try {this.monitor.wait();}catch (InterruptedException ex) {// Re-interrupt current thread, to allow other threads to react.Thread.currentThread().interrupt();interrupted = true;}}if (debug) {logger.debug('Entering throttle at concurrency count ' + this.concurrencyCount);}this.concurrencyCount++;}}}

2.SimpleAsyncTaskExecutor限流實(shí)現(xiàn):首先任務(wù)進(jìn)來,會(huì)循環(huán)判斷當(dāng)前執(zhí)行線程數(shù)是否超過concurrencyLimit,如果超了,則當(dāng)前線程調(diào)用wait方法,釋放monitor對(duì)象鎖,進(jìn)入等待狀態(tài)。

protected void beforeAccess() {if (this.concurrencyLimit == NO_CONCURRENCY) {throw new IllegalStateException('Currently no invocations allowed - concurrency limit set to NO_CONCURRENCY');}if (this.concurrencyLimit > 0) {boolean debug = logger.isDebugEnabled();synchronized (this.monitor) {boolean interrupted = false;while (this.concurrencyCount >= this.concurrencyLimit) {if (interrupted) {throw new IllegalStateException('Thread was interrupted while waiting for invocation access, ' +'but concurrency limit still does not allow for entering');}if (debug) {logger.debug('Concurrency count ' + this.concurrencyCount +' has reached limit ' + this.concurrencyLimit + ' - blocking');}try {this.monitor.wait();}catch (InterruptedException ex) {// Re-interrupt current thread, to allow other threads to react.Thread.currentThread().interrupt();interrupted = true;}}if (debug) {logger.debug('Entering throttle at concurrency count ' + this.concurrencyCount);}this.concurrencyCount++;}}}

線程任務(wù)執(zhí)行完畢后,當(dāng)前執(zhí)行線程數(shù)會(huì)減一,會(huì)調(diào)用monitor對(duì)象的notify方法,喚醒等待狀態(tài)下的線程,等待狀態(tài)下的線程會(huì)競(jìng)爭monitor鎖,競(jìng)爭到,會(huì)繼續(xù)執(zhí)行線程任務(wù)。

protected void afterAccess() {if (this.concurrencyLimit >= 0) {synchronized (this.monitor) {this.concurrencyCount--;if (logger.isDebugEnabled()) {logger.debug('Returning from throttle at concurrency count ' + this.concurrencyCount);}this.monitor.notify();}}}

雖然看了源碼了解了SimpleAsyncTaskExecutor有限流機(jī)制,實(shí)踐出真知,我們還是測(cè)試下:一、測(cè)試未開啟限流機(jī)制下,我們啟動(dòng)20個(gè)線程去調(diào)用異步方法,查看Java VisualVM工具如下:

springboot中@Async默認(rèn)線程池導(dǎo)致OOM問題

二、測(cè)試開啟限流機(jī)制,開啟限流機(jī)制的代碼如下:

@Configuration@EnableAsyncpublic class AsyncCommonConfig extends AsyncConfigurerSupport { @Override public Executor getAsyncExecutor() { SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor(); //設(shè)置允許同時(shí)執(zhí)行的線程數(shù)為10 executor.setConcurrencyLimit(10); return executor; }}

同樣,我們啟動(dòng)20個(gè)線程去調(diào)用異步方法,查看Java VisualVM工具如下:

springboot中@Async默認(rèn)線程池導(dǎo)致OOM問題

通過上面驗(yàn)證可知:1.開啟限流情況下,能有效控制應(yīng)用線程數(shù)2.雖然可以有效控制線程數(shù),但執(zhí)行效率會(huì)降低,會(huì)出現(xiàn)主線程等待,線程競(jìng)爭的情況。3.限流機(jī)制適用于任務(wù)處理比較快的場(chǎng)景,對(duì)于應(yīng)用處理時(shí)間比較慢的場(chǎng)景并不適用。==

最終解決辦法:1.自定義線程池,使用LinkedBlockingQueue阻塞隊(duì)列來限定線程池的上限2.定義拒絕策略,如果隊(duì)列滿了,則拒絕處理該任務(wù),打印日志,代碼如下:

public class AsyncConfig implements AsyncConfigurer{ private Logger logger = LogManager.getLogger(); @Value('${thread.pool.corePoolSize:10}') private int corePoolSize; @Value('${thread.pool.maxPoolSize:20}') private int maxPoolSize; @Value('${thread.pool.keepAliveSeconds:4}') private int keepAliveSeconds; @Value('${thread.pool.queueCapacity:512}') private int queueCapacity; @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setKeepAliveSeconds(keepAliveSeconds); executor.setQueueCapacity(queueCapacity); executor.setRejectedExecutionHandler((Runnable r, ThreadPoolExecutor exe) -> {logger.warn('當(dāng)前任務(wù)線程池隊(duì)列已滿.'); }); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable ex , Method method , Object... params) {logger.error('線程池執(zhí)行任務(wù)發(fā)生未知異常.', ex); } }; }}

到此這篇關(guān)于springboot中@Async默認(rèn)線程池導(dǎo)致OOM問題的文章就介紹到這了,更多相關(guān)springboot @Async線程池導(dǎo)致OOM內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 国产97色| 手机看片久久 | 日韩欧美高清在线 | 四虎精品在线观看 | 国产精品美女www爽爽爽 | 色午夜视频| 国产啊v在线观看 | 国产精品视频成人 | 高清成人综合 | 黄色三级免费 | 在线观看毛片视频 | 91最新地址 | 欧美在线免费播放 | 国产精品亚洲视频 | 亚洲精品大片 | 日韩欧美一级片 | 91大神福利视频 | 久久精品99国产精 | 麻豆精品一区二区三区视频 | 六月综合激情 | 一级片麻豆 | 国产精品乱码一区二区视频 | 91视频最新 | 欧美色图自拍 | 国产专区在线播放 | 亚洲精品在线观看免费 | 亚洲精品视频免费看 | 成av在线 | 久久久999成人 | 猫咪av网址 | 亚洲精选一区 | 天天操 夜夜操 | 91在线小视频 | 一区二区三区免费在线观看视频 | 日韩一区二区久久 | 26uuu亚洲国产精品 | 日韩成人免费在线观看 | 天天干夜夜 | 好男人www社区在线视频夜恋 | 日韩精品一区二区三区在线观看 | 亚洲视频在线观看 |