欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

怎么探討RPC框架中的服務(wù)線程隔離

本篇文章給大家分享的是有關(guān)怎么探討RPC框架中的服務(wù)線程隔離,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。

成都創(chuàng)新互聯(lián)主營(yíng)隆堯網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,重慶App定制開(kāi)發(fā),隆堯h5小程序制作搭建,隆堯網(wǎng)站營(yíng)銷(xiāo)推廣歡迎隆堯等地區(qū)企業(yè)咨詢

微服務(wù)如今應(yīng)當(dāng)是一個(gè)優(yōu)秀的程序員必須學(xué)習(xí)的一種架構(gòu)思想,而RPC框架作為微服務(wù)的核心,不說(shuō)讀一遍源碼吧,起碼它的實(shí)現(xiàn)原理還是應(yīng)該知道的。

然而目前的RPC服務(wù)框架,大多存在一個(gè)問(wèn)題,就是當(dāng)服務(wù)提供端Provider應(yīng)用中,有的服務(wù)流量大,耗時(shí)長(zhǎng),導(dǎo)致線程池資源被這些服務(wù)占盡,從而影響同一應(yīng)用中的其他服務(wù)正常提供。為此,下面主要介紹一下我對(duì)于這方面的思考。

 

前言

在進(jìn)入正文之前,可以先看一下阿里中間件島風(fēng)大佬的這篇博文(傳送門(mén)),這篇博文復(fù)現(xiàn)了Dubbo應(yīng)用中,線程池耗盡的場(chǎng)景。這其實(shí)在線上是十分普遍,解決方法無(wú)非是根據(jù)業(yè)務(wù)調(diào)整參數(shù),或者引入其他的限流、資源隔離框架,例如Hystrix、Sentinel等,使得資源間互不干擾。其實(shí)本身Dubbo也可以對(duì)不同的服務(wù)配置不同的業(yè)務(wù)線程池(通過(guò)配置protocol)從而實(shí)現(xiàn)服務(wù)的資源隔離,但是這種方式的弊端在于,一旦服務(wù)增多,線程數(shù)量會(huì)迅速膨脹。線程池過(guò)多不便于統(tǒng)一管理,同時(shí)過(guò)多的線程所帶來(lái)過(guò)多的上下文切換也會(huì)影響服務(wù)器性能。

在絕大多數(shù)場(chǎng)景下,對(duì)服務(wù)資源的隔離可以通過(guò)開(kāi)源框架Sentinel來(lái)實(shí)現(xiàn),其通過(guò)配置某個(gè)服務(wù)的并發(fā)數(shù),來(lái)達(dá)到限流和線程資源隔離的目的。坦白的講,這已經(jīng)能夠滿足絕大多數(shù)需求了,但是手動(dòng)取配置這些參數(shù)還是比較有難度的,大多得靠大佬們的經(jīng)驗(yàn)了,而且也不夠靈活。

我在學(xué)習(xí)的時(shí)候,也突發(fā)奇想,有沒(méi)有可能不依賴外部的組件,而實(shí)現(xiàn)內(nèi)部的服務(wù)資源隔離?再更進(jìn)一步,有沒(méi)有可能根據(jù)應(yīng)用內(nèi)各個(gè)服務(wù)的流量數(shù)據(jù),對(duì)每個(gè)服務(wù)資源進(jìn)行動(dòng)態(tài)的分配和綁定呢?

打個(gè)比方說(shuō),某個(gè)應(yīng)用里存在A、B兩個(gè)服務(wù),100個(gè)線程。白天的時(shí)候,A服務(wù)的流量大,B服務(wù)的流量很小,那么在這個(gè)時(shí)間段內(nèi),我們的應(yīng)用分配給A的資源理應(yīng)更多。但是也不能全給A拿走了,B也得喝口湯,不然又會(huì)出現(xiàn)線程耗盡的情況,所以此時(shí)我們可能根據(jù)流量數(shù)據(jù)的比對(duì)分給A服務(wù)80個(gè)線程,B服務(wù)20個(gè)線程;而到了晚上,A服務(wù)沒(méi)啥人用了,B服務(wù)流量來(lái)了,那我們就給B更多的資源,但也要保證A可用,比如說(shuō),A服務(wù)20線程,B服務(wù)80線程。

我承認(rèn)我一開(kāi)始只是想簡(jiǎn)單寫(xiě)個(gè)RPC框架,學(xué)習(xí)實(shí)現(xiàn)原理而已。但突然有了這樣一個(gè)想法,我就來(lái)了動(dòng)力,想看看自己的想法行不行得通,下面我便介紹下我的思考,說(shuō)的有不對(duì)的地方也歡迎大家指出和探討。

 

線程隔離的三個(gè)組件

借鑒了傳統(tǒng)的RPC框架的實(shí)現(xiàn)原理后,我們只需要修改或者增加三樣?xùn)|西,就可以完成上述的功能,分別為:線程池、數(shù)據(jù)監(jiān)控節(jié)點(diǎn)Metric和線程動(dòng)態(tài)分配的Monitor。這三者之間的關(guān)系可以先看一下這張圖有個(gè)大概的印象。

怎么探討RPC框架中的服務(wù)線程隔離

 

線程池

首先需要修改的自然是線程池。以Dubbo為例,其默認(rèn)的線程池為fixed線程池,io線程接收到請(qǐng)求后,委托Dubbo線程池完成后續(xù)的處理,通過(guò)調(diào)用ExecutorService.execute。

但是在這里,使用JDK中的線程池顯然是行不通了。線程池中的Thread也不再是單純的Thread,而需要更進(jìn)一步的抽象。這里參考Netty中NioEventLoop的設(shè)計(jì)思想,將每條Thread抽象為一條Loop,其既是任務(wù)執(zhí)行的本體Thread,也是ExecutorService的抽象,而所有Loop交由LoopGroup統(tǒng)一管理,由LoopGroup決定將任務(wù)提交至哪一個(gè)線程。這里我實(shí)現(xiàn)的比較簡(jiǎn)單,每個(gè)線程有個(gè)專(zhuān)屬的id,通過(guò)拿到線程的id,將任務(wù)提交到對(duì)應(yīng)的線程,原理可以參考下圖:

怎么探討RPC框架中的服務(wù)線程隔離

私以為核心在于維護(hù)服務(wù)與線程id的對(duì)應(yīng)關(guān)系,以及在請(qǐng)求到來(lái)時(shí),LoopGroup會(huì)根據(jù)請(qǐng)求中服務(wù)的類(lèi)型,選擇對(duì)應(yīng)id的線程,并交由該線程去處理請(qǐng)求。

 

數(shù)據(jù)監(jiān)控

數(shù)據(jù)的監(jiān)控相對(duì)來(lái)說(shuō)是最好辦的。這里我參考了Sentinel的實(shí)現(xiàn),使用時(shí)間窗口法統(tǒng)計(jì)各個(gè)服務(wù)的流量數(shù)據(jù),包括pass、success、rt、reject、excetpion等。(關(guān)于Sentinel中的時(shí)間窗口,后面有時(shí)間再專(zhuān)門(mén)寫(xiě)篇源碼分析)

而至于監(jiān)控節(jié)點(diǎn)的形式,根據(jù)調(diào)用鏈路的具體實(shí)現(xiàn)不同,在Dubbo中可以是一個(gè)filter,而我因?yàn)閷⒄{(diào)用鏈路抽象為一個(gè)Pipeline,所以它作為Pipeline上的一個(gè)節(jié)點(diǎn),參考下圖:

怎么探討RPC框架中的服務(wù)線程隔離

這里貼上MetricContext的關(guān)鍵源碼:

//處理請(qǐng)求時(shí),pass+1,同時(shí)記錄開(kāi)始時(shí)間并保存在線程上下文中
@Override
protected void handle(Object obj) {
   if(obj instanceof RpcRequest){
       RpcContext rpcContext=RpcContext.getContext();
       rpcContext.setStartTime(TimeUtil.currentTimeMillis());
       paladinMetric.addPass(1);
   }
}

//響應(yīng)請(qǐng)求時(shí),說(shuō)明請(qǐng)求處理正常,則通過(guò)線程上下文拿到開(kāi)始時(shí)間,
//計(jì)算出響應(yīng)時(shí)間rt后將rt寫(xiě)入統(tǒng)計(jì)數(shù)據(jù),同時(shí)success+1
@Override
protected void response(Object obj) {
       RpcContext rpcContext=RpcContext.getContext();
       Long startTime=rpcContext.getStartTime();
       if(startTime!=null){
           Long rt=TimeUtil.currentTimeMillis()-startTime;
           paladinMetric.addRT(rt);
           paladinMetric.addSuccess(1);
           logger.warn(rpcContext.getRpcRequest().getClassName()
                   +":"
                   +rpcContext.getRpcRequest().getMethodName()
                   +" 's RT is "
                   +rt);
       }else{
           logger.error(rpcContext.getRpcRequest().getClassName()
                   +":"
                   +rpcContext.getRpcRequest().getMethodName()
                   +"has no start time!");
       }
}

//這里就是統(tǒng)一處理異常的方法,區(qū)分為普通異常和拒絕異常,
//如果是拒絕異常,說(shuō)明線程已滿,拒絕添加任務(wù),reject+1
@Override
protected void caughtException(Object obj) {
   paladinMetric.addException(1);
   if(obj instanceof RejectedExecutionException){
       paladinMetric.addReject(1);
   }
}
 

每個(gè)Context都會(huì)繼承AbstractContext,只需要實(shí)現(xiàn)handle、response和caughtException方法即可,由AbstractContext屏蔽了底層pipeline的順序調(diào)用。

 

線程分配

最后就是如何動(dòng)態(tài)的將線程分配給服務(wù)。在這里,我們需要抽象一個(gè)評(píng)價(jià)模型,去評(píng)估各個(gè)服務(wù)應(yīng)該占用多少資源(線程),可以參考下圖:

怎么探討RPC框架中的服務(wù)線程隔離

簡(jiǎn)單來(lái)說(shuō),由于監(jiān)控節(jié)點(diǎn)的存在,我們很容易就拿到每個(gè)服務(wù)的流量數(shù)據(jù),然后抽象出每一個(gè)服務(wù)的評(píng)價(jià)模型,最后通過(guò)某種策略,得到線程分配的結(jié)果。

同時(shí)服務(wù)-線程的對(duì)應(yīng)關(guān)系的讀寫(xiě),顯然是一個(gè)讀多寫(xiě)少的場(chǎng)景。可以后臺(tái)開(kāi)啟一個(gè)線程,每隔一段時(shí)間(比如20s),執(zhí)行一次動(dòng)態(tài)分配的策略。采用CopyOnWrite的思想,將對(duì)應(yīng)關(guān)系的引用用volatile修飾,線程重新分配完成之后,直接替換掉其引用即可,這樣對(duì)性能的影響便沒(méi)有那么大了。

這里的問(wèn)題在于,如何合理的制定分配的策略。由于我實(shí)在缺乏相應(yīng)的經(jīng)驗(yàn),所以寫(xiě)的比較撈,希望有大佬可以指點(diǎn)一二。

 

效果如何

說(shuō)了這么多,那我們便來(lái)看看效果如何。代碼我都放在了github上(由于時(shí)間比較短再加上本人菜,寫(xiě)得比較粗糙,請(qǐng)大家見(jiàn)諒T T),代碼樣例都在paladin-demo模塊中,這里我就直接上結(jié)果了。

先定義一下參數(shù),線程數(shù)總共20,每個(gè)服務(wù)最少能分配線程數(shù)為5,每條線程的阻塞隊(duì)列容量為4,服務(wù)端兩個(gè)服務(wù),一個(gè)阻塞時(shí)間長(zhǎng),另一個(gè)無(wú)阻塞。

這里先定義一個(gè)阻塞時(shí)間長(zhǎng)的服務(wù)HelloWorld。

怎么探討RPC框架中的服務(wù)線程隔離

然后我們通過(guò)http請(qǐng)求觸發(fā)任務(wù),模擬大流量請(qǐng)求。

怎么探討RPC框架中的服務(wù)線程隔離

同時(shí)給出一個(gè)無(wú)阻塞的服務(wù)HelloPaladin,可以通過(guò)http訪問(wèn)。

怎么探討RPC框架中的服務(wù)線程隔離

先后啟動(dòng)服務(wù)服務(wù)提供端和消費(fèi)端,開(kāi)啟任務(wù)。控制臺(tái)直接炸裂。

怎么探討RPC框架中的服務(wù)線程隔離

服務(wù)瘋狂拋出拒絕異常。我們?cè)佥斎雔ocalhost:8080/helloPaladin?value=lalala,多點(diǎn)幾次,可以發(fā)現(xiàn)頁(yè)面很快就能返回結(jié)果,這也意味著這個(gè)服務(wù)并沒(méi)有被干擾。

最后我們來(lái)看一下,在任務(wù)啟動(dòng)后,線程分配的情況如何:

22:15:06,653  INFO PaladinMonitor:81 - totalScore: 594807
22:15:06,653  INFO PaladinMonitor:91 - service: com.lcf.HelloPaladin:1.0.0_paladin, score: 1646
22:15:06,653  INFO PaladinMonitor:91 - service: com.lcf.HelloWorld:1.0.0_paladin, score: 593161
22:15:06,654  INFO PaladinMonitor:113 - Threads re-distribution result: {com.lcf.HelloPaladin:1.0.0_paladin=[1, 2, 3, 4, 5], com.lcf.HelloWorld:1.0.0_paladin=[6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]}
 

第一行輸出的是所有服務(wù)總共的分?jǐn)?shù),接下來(lái)兩行分別是兩個(gè)服務(wù)所得到的分?jǐn)?shù),最后一行是線程分配之后的結(jié)果。

我們穿插調(diào)用的HelloPaladin服務(wù)得到的分?jǐn)?shù)遠(yuǎn)遠(yuǎn)低于跑任務(wù)的服務(wù)HelloWorld,但是由于設(shè)置了最小線程數(shù),所以HelloPaladin服務(wù)分到了5條線程,而HelloWorld服務(wù)占據(jù)了其余的線程。(這里由于還開(kāi)啟了一個(gè)單線程服務(wù),所以沒(méi)有0號(hào)線程,至于什么是單線程服務(wù)可以看后文)

可以看到,服務(wù)間的線程資源確實(shí)隔離了,某一個(gè)服務(wù)的不可用不會(huì)影響到其他服務(wù),同時(shí)資源也會(huì)向大流量的服務(wù)傾斜。

 

更花哨的玩法

在實(shí)現(xiàn)上面的功能之后,或許還有更加花哨的玩法。考慮這樣一個(gè)場(chǎng)景,如果某個(gè)服務(wù)存在頻繁加鎖的場(chǎng)景,那么多個(gè)線程并發(fā)加鎖執(zhí)行,未必會(huì)有單個(gè)線程串行無(wú)鎖執(zhí)行來(lái)的效率高,畢竟鎖和線程切換的開(kāi)銷(xiāo)也不容忽視。

在實(shí)現(xiàn)了服務(wù)與線程的對(duì)應(yīng)關(guān)系之后,這種串行無(wú)鎖執(zhí)行的思路就很容易實(shí)現(xiàn)了,在初始化的時(shí)候,直接分配給這個(gè)服務(wù)固定的線程id號(hào)即可,這個(gè)線程也不會(huì)參與后續(xù)的動(dòng)態(tài)分配流程。可以通過(guò)注解參數(shù)的方式來(lái)實(shí)現(xiàn):


@RpcService(type = RpcConstans.SINGLE)
public class HelloSynWorldImpl implements HelloSynWorld
 

就是這么簡(jiǎn)單,服務(wù)器啟動(dòng)之后你就會(huì)發(fā)現(xiàn),這個(gè)服務(wù)都會(huì)使用某條固定的線程去執(zhí)行,自然也就用不著加鎖了(除非要跟其他服務(wù)同時(shí)操作共享資源,那就不適用于這種場(chǎng)景),不過(guò)這種串行場(chǎng)景我想了想,好像并不多,只有在那種純內(nèi)存的操作中可能會(huì)比較有性能優(yōu)勢(shì)(是不是很像redis),所以也就圖一樂(lè)。

 

相比原來(lái)的線程模型有何優(yōu)劣?

話又說(shuō)回來(lái)了,雖然解決了服務(wù)資源隔離和分配的問(wèn)題,那么相比原來(lái)的線程模型是否就沒(méi)有劣勢(shì)了呢?

因?yàn)榧尤肓烁嗟慕M件,考慮到監(jiān)控節(jié)點(diǎn)的性能損耗,增加了分配線程、選擇線程的邏輯,或許在性能上相比原來(lái)的線程模型會(huì)差一點(diǎn),至于差多少,我可能也沒(méi)法定量給出解答,還需要進(jìn)一步的測(cè)試。不過(guò)可以肯定的是,可以通過(guò)更多的優(yōu)化,使得兩者的性能更加接近,例如:用JcTool的無(wú)鎖隊(duì)列替換JDK中的阻塞隊(duì)列;給出合適的評(píng)價(jià)模型,使得資源分配更合理以及分配過(guò)程性能更優(yōu)等等。

當(dāng)然最關(guān)鍵的還是你業(yè)務(wù)代碼寫(xiě)的咋樣,畢竟框架優(yōu)化的再好,業(yè)務(wù)代碼不大行,那點(diǎn)優(yōu)化效果微乎其微。

以上就是怎么探討RPC框架中的服務(wù)線程隔離,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

當(dāng)前標(biāo)題:怎么探討RPC框架中的服務(wù)線程隔離
文章位置:http://www.chinadenli.net/article20/gcehco.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動(dòng)網(wǎng)站建設(shè)商城網(wǎng)站網(wǎng)站營(yíng)銷(xiāo)響應(yīng)式網(wǎng)站關(guān)鍵詞優(yōu)化企業(yè)建站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

綿陽(yáng)服務(wù)器托管