新聞中心
在很久很久之前,我有一段痛苦的記憶。那種被故障所驅(qū)使的感覺(jué),在我腦海里久久無(wú)法驅(qū)散。

覃塘ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)的ssl證書(shū)銷(xiāo)售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話(huà)聯(lián)系或者加微信:18982081108(備注:SSL證書(shū)合作)期待與您的合作!
原因無(wú)它,有小伙伴開(kāi)啟了線(xiàn)程池的暴力使用模式。沒(méi)錯(cuò),就是下面這篇文章。
奪命故障 ! 炸出了投資人!
我有必要簡(jiǎn)單的復(fù)述一下。其主要原因,就是開(kāi)發(fā)人員,在每一次方法調(diào)用里,都創(chuàng)建了一個(gè)單獨(dú)的線(xiàn)程池去處理。這樣的話(huà),如果請(qǐng)求量一增加,整個(gè)操作系統(tǒng)的壓力就會(huì)耗盡,最終所有的業(yè)務(wù)都無(wú)法響應(yīng)。
我一直認(rèn)為這是一個(gè)非常偶發(fā)的低級(jí)錯(cuò)誤,發(fā)生頻率非常的低。但隨著這樣的故障越來(lái)越多,xjjdog認(rèn)識(shí)到這是一個(gè)普遍的現(xiàn)象。
以異步性能優(yōu)化為目的,反而帶來(lái)的整體業(yè)務(wù)不可用的結(jié)果,是非常打臉的一種優(yōu)化。
1.Spring的異步代碼
Spring作為Java屆的杠把子框架,其過(guò)度封裝的API深得開(kāi)發(fā)人員的喜愛(ài)。根據(jù)語(yǔ)義化編程的邏輯,只要某些關(guān)鍵字在語(yǔ)言層面上過(guò)得去,我們就可以把它給加上去。比如@Async注解。
我永遠(yuǎn)想不通是什么給了開(kāi)發(fā)人員勇氣,去加上這個(gè)@Async注解,因?yàn)檫@種涉及到多線(xiàn)程的東西,即使是自己去創(chuàng)建線(xiàn)程,也是心懷敬畏,唯恐?jǐn)_了操作系統(tǒng)的安寧。@Async這樣的黑盒,真的可以那么順暢的使用么?
我們不妨debug一下代碼,讓子彈飛一會(huì)兒。
首先,生成一個(gè)小小的項(xiàng)目,然后在主類(lèi)上加上必須的注解。嗯,別忘了這一環(huán),否則你后面加的注解將沒(méi)什么用處。
@SpringBootApplication
@EnableAsync
public class DemoApplication {
創(chuàng)造一個(gè)帶@Async注解的方法。
@Component
public class AsyncService {
@Async
public void async(){
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread());
}catch (Exception ex){
ex.printStackTrace();
}
}
}
然后,做一個(gè)對(duì)應(yīng)的test接口,訪(fǎng)問(wèn)時(shí)會(huì)調(diào)用這個(gè)async方法。
@ResponseBody
@GetMapping("test")
public void test(){
service.async();
}
訪(fǎng)問(wèn)時(shí),直接打個(gè)斷點(diǎn),即可獲取執(zhí)行異步線(xiàn)程的線(xiàn)程池。
可以看到,異步任務(wù)使用了一個(gè)線(xiàn)程池,它的corePoolSize=8, 阻塞隊(duì)列采用了無(wú)界隊(duì)列LinkedBlockingQueue。一旦采用了這樣的組合,最大線(xiàn)程數(shù)就會(huì)形同虛設(shè),因?yàn)槌?個(gè)線(xiàn)程的任務(wù),將全部會(huì)被放到無(wú)界隊(duì)列里。使得下面的代碼變成了擺設(shè)。
throw new TaskRejectedException("Executor [" + executor + "] did not accept
task: " + task, var4);
如果你的訪(fǎng)問(wèn)量非常大,這些任務(wù)將全部堆積在LinkedBlockingQueue里。情況好一點(diǎn)的,這些任務(wù)的執(zhí)行會(huì)變得延遲很大;情況壞一點(diǎn)的,任務(wù)太多將直接造成內(nèi)存溢出OOM!
你可能會(huì)說(shuō),我可以自己指定另外一個(gè)ThreadPoolExceute,然后使用@Async注解來(lái)聲明啊。說(shuō)這話(huà)的同學(xué),一定是能力比較強(qiáng),或者Review的代碼比較少,沒(méi)有經(jīng)過(guò)豬隊(duì)友的洗禮。
2.是SpringBoot救了你
SpringBoot是個(gè)好東西。
在TaskExecutionAutoConfiguration中,通過(guò)生成ThreadPoolTaskExecutor的Bean,來(lái)提供默認(rèn)的Executor。
@ConditionalOnMissingBean({Executor.class})
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
return builder.build();
}
也就是我們上面所說(shuō)的那個(gè)。如果沒(méi)有SpringBoot的助力,Spring將默認(rèn)使用SimpleAsyncTaskExecutor。
參見(jiàn)
org.springframework.aop.interceptor.AsyncExecutionInterceptor。
@Override
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
}
這就是Spring大仙所干的事。
SimpleAsyncTaskExecutor類(lèi)設(shè)計(jì)的非常操蛋,因?yàn)樗繄?zhí)行一次,都會(huì)創(chuàng)建一個(gè)單獨(dú)的線(xiàn)程,根本沒(méi)有共用線(xiàn)程池。比如你的TPS是1000,異步執(zhí)行了任務(wù),那么你每秒將會(huì)生成1000個(gè)線(xiàn)程!
這明顯是想要累死操作系統(tǒng)的節(jié)奏。
protected void doExecute(Runnable task) {
Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
thread.start();
}
3.End
明眼人一看,這種使用new線(xiàn)程的處理方式將會(huì)是非??膳碌摹5湍肧pring本身來(lái)說(shuō),引用SimpleAsyncTaskExecutor這個(gè)類(lèi)的地方還不少,包括比較流行的AsyncRestTemplate。
這暴露了很多風(fēng)險(xiǎn),尤其是竟然在這些列表中看到了redis的身影。這個(gè)類(lèi)的設(shè)計(jì),使得任務(wù)的執(zhí)行變的非常的不可控。
看這個(gè)API,我感覺(jué)Spring是進(jìn)入了設(shè)計(jì)的魔怔狀態(tài)。
這個(gè)東西的隱藏bug可能還會(huì)更深!比如org.springframework.context.event.EventListener注解,用于實(shí)現(xiàn)DDD那套所謂的事件驅(qū)動(dòng)模式,有不少框架直接set了AsyncRestTemplate,那么就等死吧。
趕緊把SimpleAsyncTaskExecutor加入你的API黑名單,或者埋坑清單吧!
創(chuàng)建線(xiàn)程有那么難么?需要使用Spring創(chuàng)建的線(xiàn)程?有時(shí)候我實(shí)在是想不通,暴露出這樣的接口目的是為了什么。
就連原生的線(xiàn)程池我們還沒(méi)搞明白呢,你還給包了一層,這是方便我們甩鍋啊!
作者簡(jiǎn)介:小姐姐味道 (xjjdog),一個(gè)不允許程序員走彎路的公眾號(hào)。聚焦基礎(chǔ)架構(gòu)和Linux。十年架構(gòu),日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。
文章題目:強(qiáng)烈反對(duì)使用Spring封裝的多線(xiàn)程類(lèi)!
地址分享:http://m.fisionsoft.com.cn/article/cdidshe.html


咨詢(xún)
建站咨詢(xún)
