日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

你的接口能承受高并发吗?

發(fā)布時(shí)間:2025/3/21 编程问答 11 豆豆
生活随笔 收集整理的這篇文章主要介紹了 你的接口能承受高并发吗? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

肥朝?| 作者

轉(zhuǎn)載自「肥朝」公眾號(hào)

本篇主要講解的是前陣子的一個(gè)壓測(cè)問(wèn)題.那么就直接開(kāi)門(mén)見(jiàn)山

可能有的朋友不并不知道forceTransactionTemplate這個(gè)是干嘛的,首先這里先普及一下,在Java中,我們一般開(kāi)啟事務(wù)就有三種方式

  • XML中根據(jù)service及方法名配置切面,來(lái)開(kāi)啟事務(wù)(前幾年用的頻率較高,現(xiàn)在基本很少用)

  • @Transactional注解開(kāi)啟事務(wù)(使用頻率最高)

  • 采用spring的事務(wù)模板(截圖中的方式,幾乎沒(méi)什么人用)

我們先不糾結(jié)為什么使用第三種,后面在講事務(wù)傳播機(jī)制的時(shí)候我會(huì)專(zhuān)門(mén)介紹,我們聚焦一下主題,你現(xiàn)在只要知道,那個(gè)是開(kāi)啟事務(wù)的意思就行了.我特意用紅色和藍(lán)色把日志代碼圈起來(lái),意思就是,進(jìn)入方法的時(shí)候打印日志,然后開(kāi)啟事務(wù)后,再打印一個(gè)日志.一波壓測(cè)之后,發(fā)現(xiàn)接口頻繁超時(shí),數(shù)據(jù)一致壓不上去.我們查看日志如下:

我們發(fā)現(xiàn).這兩個(gè)日志輸出時(shí)間間隔,竟然用了接近5秒!開(kāi)個(gè)事務(wù)為何用了5秒?事出反常必有妖!

如何切入解決問(wèn)題

線(xiàn)上遇到高并發(fā)的問(wèn)題,由于一般高并發(fā)問(wèn)題重現(xiàn)難度比較大,所以一般肥朝都是采用眼神編譯,九淺一深靜態(tài)看源碼的方式來(lái)分析.具體可以參考本地可跑,上線(xiàn)就崩?慌了!.但是考慮到肥朝公眾號(hào)仍然有小部分新關(guān)注的粉絲尚未掌握分析問(wèn)題的技巧,本篇就再講一些遇到此類(lèi)問(wèn)題的一些常見(jiàn)分析方式,不至于遇到問(wèn)題時(shí),慌得一比!

好在這個(gè)并發(fā)問(wèn)題的難度并不大,本篇案例排查非常適合小白入門(mén),我們可以通過(guò)本地模擬場(chǎng)景重現(xiàn),將問(wèn)題范圍縮小,從而逐步定位問(wèn)題.

本地重現(xiàn)

首先我們可以準(zhǔn)備一個(gè)并發(fā)工具類(lèi),通過(guò)這個(gè)工具類(lèi),可以在本地環(huán)境模擬并發(fā)場(chǎng)景.手機(jī)查看代碼并不友好,但是沒(méi)關(guān)系,以下代碼均是給你復(fù)制粘貼進(jìn)項(xiàng)目重現(xiàn)問(wèn)題用的,并不是給你手機(jī)上看的.至于這個(gè)工具類(lèi)為什么能模擬并發(fā)場(chǎng)景,由于這個(gè)工具類(lèi)的代碼全是JDK中的代碼,核心就是CountDownLatch類(lèi),這個(gè)原理你根據(jù)我提供的關(guān)鍵字對(duì)著你喜歡的搜索引擎搜索即可.

CountDownLatchUtil.java

public?class?CountDownLatchUtil?{private?CountDownLatch?start;private?CountDownLatch?end;private?int?pollSize?=?10;public?CountDownLatchUtil()?{this(10);}public?CountDownLatchUtil(int?pollSize)?{this.pollSize?=?pollSize;start?=?new?CountDownLatch(1);end?=?new?CountDownLatch(pollSize);}public?void?latch(MyFunctionalInterface?functionalInterface)?throws?InterruptedException?{ExecutorService?executorService?=?Executors.newFixedThreadPool(pollSize);for?(int?i?=?0;?i?<?pollSize;?i++)?{Runnable?run?=?new?Runnable()?{@Overridepublic?void?run()?{try?{start.await();functionalInterface.run();}?catch?(InterruptedException?e)?{e.printStackTrace();}?finally?{end.countDown();}}};executorService.submit(run);}start.countDown();end.await();executorService.shutdown();}@FunctionalInterfacepublic?interface?MyFunctionalInterface?{void?run();} }

HelloService.java

public?interface?HelloService?{void?sayHello(long?timeMillis);}

HelloServiceImpl.java

@Service public?class?HelloServiceImpl?implements?HelloService?{private?final?Logger?log?=?LoggerFactory.getLogger(HelloServiceImpl.class);@Transactional@Overridepublic?void?sayHello(long?timeMillis)?{long?time?=?System.currentTimeMillis()?-?timeMillis;if?(time?>?5000)?{//超過(guò)5秒的打印日志輸出log.warn("time?:?{}",?time);}try?{//模擬業(yè)務(wù)執(zhí)行時(shí)間為1sThread.sleep(1000);}?catch?(Exception?e)?{e.printStackTrace();}} }

HelloServiceTest.java

@RunWith(SpringRunner.class) @SpringBootTest public?class?HelloServiceTest?{@Autowiredprivate?HelloService?helloService;@Testpublic?void?testSayHello()?throws?Exception?{long?currentTimeMillis?=?System.currentTimeMillis();//模擬1000個(gè)線(xiàn)程并發(fā)CountDownLatchUtil?countDownLatchUtil?=?new?CountDownLatchUtil(1000);countDownLatchUtil.latch(()?->?{helloService.sayHello(currentTimeMillis);});}}

我們從本地調(diào)試的日志中,發(fā)現(xiàn)了大量超過(guò)5s的接口,并且還有一些規(guī)律,肥朝特地用不同顏色的框框給大家框起來(lái)

為什么這些時(shí)間,都是5個(gè)為一組,且每組數(shù)據(jù)相差是1s左右呢?

真相大白

@Transactional的核心代碼如下(后續(xù)我會(huì)專(zhuān)門(mén)一個(gè)系列分析這部分源碼,關(guān)注肥朝以免錯(cuò)過(guò)核心內(nèi)容).這里簡(jiǎn)單說(shuō)就是retVal = invocation.proceedWithInvocation()方法會(huì)去獲取數(shù)據(jù)庫(kù)連接.

if?(txAttr?==?null?||?!(tm?instanceof?CallbackPreferringPlatformTransactionManager))?{//?Standard?transaction?demarcation?with?getTransaction?and?commit/rollback?calls.TransactionInfo?txInfo?=?createTransactionIfNecessary(tm,?txAttr,?joinpointIdentification);Object?retVal?=?null;try?{//?This?is?an?around?advice:?Invoke?the?next?interceptor?in?the?chain.//?This?will?normally?result?in?a?target?object?being?invoked.retVal?=?invocation.proceedWithInvocation();}catch?(Throwable?ex)?{//?target?invocation?exceptioncompleteTransactionAfterThrowing(txInfo,?ex);throw?ex;}finally?{cleanupTransactionInfo(txInfo);}commitTransactionAfterReturning(txInfo);return?retVal; }

然后肥朝為了更好的演示這個(gè)問(wèn)題,將數(shù)據(jù)庫(kù)連接池(本篇用的是Druid)的參數(shù)做了以下設(shè)置

//初始連接數(shù) spring.datasource.initialSize=1 //最大連接數(shù) spring.datasource.maxActive=5

由于最大連接數(shù)是5.所以當(dāng)1000個(gè)線(xiàn)程并發(fā)進(jìn)來(lái)的時(shí)候,你可以想象是一個(gè)隊(duì)伍有1000個(gè)人排隊(duì),最前面的5個(gè),拿到了連接,并且執(zhí)行業(yè)務(wù)時(shí)間為1秒.那么隊(duì)伍中剩下的995個(gè)人,就在門(mén)外等候.等這5個(gè)執(zhí)行完的時(shí)候.釋放了5個(gè)連接,依次向后的5個(gè)人又進(jìn)來(lái),又執(zhí)行1秒的業(yè)務(wù)操作.通過(guò)簡(jiǎn)單的小學(xué)數(shù)學(xué),都可以計(jì)算出最后5個(gè)執(zhí)行完,需要多長(zhǎng)時(shí)間.通過(guò)這里分析,你就知道,為什么上面的日志輸出,是5秒為一組了,并且每組間隔為1s了.

怎么解決

看過(guò)肥朝源碼實(shí)戰(zhàn)的粉絲都知道,肥朝從來(lái)不耍流氓,凡是拋出問(wèn)題,都會(huì)相應(yīng)給出其中一種解決方案.當(dāng)然方案沒(méi)有最優(yōu)只有更優(yōu)!

比如看到這里有的朋友可能會(huì)說(shuō),你最大連接數(shù)設(shè)置得就像平時(shí)贊賞肥朝的金額一樣小,如果設(shè)置大一點(diǎn),自然就不會(huì)有問(wèn)題了.當(dāng)然這里為了方便向大家演示問(wèn)題,設(shè)置了最大連接數(shù)是5.正常生產(chǎn)的連接數(shù)是要根據(jù)業(yè)務(wù)特點(diǎn)和不斷壓測(cè)才能得出合理的值,當(dāng)然肥朝也了解到,部分同學(xué)公司機(jī)器的配置,竟然比不過(guò)市面上的千元手機(jī)!!!

但是其實(shí)當(dāng)時(shí)壓測(cè)的時(shí)候,數(shù)據(jù)庫(kù)的最大連接數(shù)設(shè)置的是200,并且當(dāng)時(shí)的壓測(cè)壓力并不大.那為什么還會(huì)有這個(gè)問(wèn)題呢?那么仔細(xì)看前面的代碼

其中這個(gè)校驗(yàn)的代碼是RPC調(diào)用,該接口的同事并沒(méi)有像肥朝一樣值得托付終身般的高度可靠,導(dǎo)致耗時(shí)時(shí)間較長(zhǎng),從而導(dǎo)致后續(xù)線(xiàn)程獲取數(shù)據(jù)庫(kù)連接等待的時(shí)間過(guò)長(zhǎng).你再根據(jù)前面說(shuō)的小學(xué)數(shù)學(xué)來(lái)算一下就很容易明白該壓測(cè)問(wèn)題出現(xiàn)的原因.

敲黑板劃重點(diǎn)

之前肥朝就反復(fù)說(shuō)過(guò),遇到問(wèn)題,要經(jīng)過(guò)深度思考.比如這個(gè)問(wèn)題,我們能得到什么拓展性的思考呢?我們來(lái)看一下之前一位粉絲的面試經(jīng)歷

其實(shí)他面試遇到的這個(gè)問(wèn)題,和我們這個(gè)壓測(cè)問(wèn)題基本是同一個(gè)問(wèn)題,只不過(guò)面試官的結(jié)論其實(shí)并不夠準(zhǔn)確.我們來(lái)一起看一下阿里巴巴的開(kāi)發(fā)手冊(cè)

那么什么樣叫做濫用呢?其實(shí)肥朝認(rèn)為,即使這個(gè)方法經(jīng)常調(diào)用,但是都是單表insert、update操作,執(zhí)行時(shí)間非常短,那么承受較大并發(fā)問(wèn)題也不大.關(guān)鍵是,這個(gè)事務(wù)中的所有方法調(diào)用,是否是有意義的,或者說(shuō),事務(wù)中的方法是否是真的要事務(wù)保證,才是關(guān)鍵.因?yàn)椴糠滞瑢W(xué),在一些比較傳統(tǒng)的公司,做的多是能用就行的CRUD工作,很容易一個(gè)service方法,就直接打上事務(wù)注解開(kāi)始事務(wù),然后在一個(gè)事務(wù)中,進(jìn)行大量和事務(wù)一毛錢(qián)關(guān)系都沒(méi)有的無(wú)關(guān)耗時(shí)操作,比如文件IO操作,比如查詢(xún)校驗(yàn)操作等.例如本文中的業(yè)務(wù)校驗(yàn)就完全沒(méi)必要放在事務(wù)中.平時(shí)工作中沒(méi)有相應(yīng)的實(shí)戰(zhàn)場(chǎng)景,加上并沒(méi)有關(guān)注肥朝的公眾號(hào),對(duì)原理源碼真實(shí)實(shí)戰(zhàn)場(chǎng)景一無(wú)所知.面試稍微一問(wèn)原理就喊痛,面試官也只好換個(gè)方向再繼續(xù)深入!

通過(guò)這個(gè)經(jīng)歷我們又有什么拓展性的思考呢?因?yàn)閱?wèn)題是永遠(yuǎn)解決不完的,但是我們可以通過(guò)不斷的思考,把這個(gè)問(wèn)題壓榨出更多的價(jià)值!我們?cè)賮?lái)看一下阿里規(guī)范手冊(cè)

用大白話(huà)概括就是,盡量減少鎖的粒度.并且盡量避免在鎖中調(diào)用RPC方法,因?yàn)镽PC方法涉及網(wǎng)絡(luò)因素,他的調(diào)用時(shí)間存在很大的不可控,很容易就造成了占用鎖的時(shí)間過(guò)長(zhǎng).

其實(shí)這個(gè)和我們這個(gè)壓測(cè)問(wèn)題是一樣的.首先你本地事務(wù)中調(diào)用RPC既不能起到事務(wù)作用(RPC需要分布式事務(wù)保證),但是又會(huì)因?yàn)镽PC不可控因素導(dǎo)致數(shù)據(jù)庫(kù)連接占用時(shí)間過(guò)長(zhǎng).從而引起接口超時(shí).當(dāng)然我們也可以通過(guò)APM工具來(lái)梳理接口的耗時(shí)拓?fù)?將此類(lèi)問(wèn)題在壓測(cè)前就暴露.

總結(jié)

以上是生活随笔為你收集整理的你的接口能承受高并发吗?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。