高并發(fā)服務(wù)優(yōu)化篇:從RPC預(yù)熱轉(zhuǎn)發(fā)看服務(wù)端性能調(diào)優(yōu)
時(shí)間:2021-09-03 10:10:08
手機(jī)看文章
掃描二維碼
隨時(shí)隨地手機(jī)看文章
[導(dǎo)讀]公眾號(hào)「Coder的技術(shù)之路」本篇帶大家來(lái)看下RPC的一些高級(jí)特性和其背后的原因。(還是以開(kāi)源的dubbo和sofa為例來(lái)說(shuō)明)。Part1RPC為了性能做了哪些努力1.1Provider分組和直連路由尋址,負(fù)載均衡是很好,可以保證流量均勻從而保護(hù)服務(wù)節(jié)點(diǎn)穩(wěn)定。但是,我們有的時(shí)候...
本文來(lái)源:公眾號(hào)「 Coder的技術(shù)之路」本篇帶大家來(lái)看下RPC的一些高級(jí)特性和其背后的原因。(還是以開(kāi)源的dubbo和sofa為例來(lái)說(shuō)明)。

摘自:www.sofastack.tech 
Future異步調(diào)用 異步調(diào)用對(duì)服務(wù)性能和并發(fā)的支持起到很大的作用。一般異步調(diào)用有Futurn和callback等方式,這里我們說(shuō)下Future的原理:調(diào)用下游之后,先返回一個(gè)Future,上游通過(guò)Future.get()方法對(duì)結(jié)果進(jìn)行獲取,如果結(jié)果未返回則會(huì)讓出CPU資源進(jìn)入等待,直到結(jié)果到達(dá)或超時(shí)后觸發(fā)回調(diào)方法才被喚醒。由于篇幅問(wèn)題,F(xiàn)uture的核心邏輯的相關(guān)注釋就不放了,之前的消息消費(fèi)順序保障的文章中也有敘述,有興趣的同學(xué)可以看下~
答:?大部分情況是有利的,不過(guò)有些特殊的場(chǎng)景,更希望多次請(qǐng)求連接到同一臺(tái)服務(wù)器。比如,有狀態(tài)的服務(wù)(很多帶數(shù)據(jù)功能的服務(wù)都是有狀態(tài)的,比如很久之前的帶登陸session的Tomcat服務(wù)、存儲(chǔ)集群服務(wù)等),其實(shí)希望每次請(qǐng)求都連接到相同的服務(wù)器。這就用到了粘滯連接功能。
Part1RPC為了性能做了哪些努力
1.1Provider分組和直連
路由尋址,負(fù)載均衡是很好,可以保證流量均勻從而保護(hù)服務(wù)節(jié)點(diǎn)穩(wěn)定。但是,我們有的時(shí)候其實(shí)不希望我們的請(qǐng)求亂跑,最好能打到指定的機(jī)器上。比如聯(lián)調(diào)和測(cè)試的時(shí)候,直連功能就顯得很重要了。只有經(jīng)歷過(guò)多方合作聯(lián)調(diào)時(shí)請(qǐng)求到處亂跑的痛,才知道分組和直連的功能對(duì)開(kāi)發(fā)是多么的友好。//以sofa為例
@Extension(value?=?"directUrl",?order?=?-20000)
@AutoActive(consumerSide?=?true)
public?class?DirectUrlRouter?extends?Router?{
??//...
}
我們可以看到直連路由策略的order屬性,被賦予了一個(gè)極小的值,變成了優(yōu)先級(jí)最高的路由策略,所以只要配置的直連列表,則會(huì)優(yōu)先走配置中的列表地址。
1.2異步調(diào)用

1.3本地優(yōu)先、遠(yuǎn)程優(yōu)先
很多時(shí)候,我們會(huì)遇到消費(fèi)端和服務(wù)端可能都是自己的情況。這個(gè)時(shí)候,在常規(guī)的路由尋址之外,又提供給我們一種調(diào)用的可能性,就是直接調(diào)用當(dāng)前服務(wù)器上的程序,這樣做的好處比較明顯,省去了網(wǎng)絡(luò)傳輸?shù)葧r(shí)間損耗,效率更高。List?localProviderInfo?=?new?ArrayList();
//?解析IP,看是否和本地一致
for?(ProviderInfo?providerInfo?:?providerInfos)?{?
????if?(localhost.equals(providerInfo.getHost()))?{
????????localProviderInfo.add(providerInfo);
????}
}
//?命中本機(jī)的服務(wù)端
if?(CommonUtils.isNotEmpty(localProviderInfo))?{?
????return?super.doSelect(invocation,?localProviderInfo);
}?else?{?
??//?沒(méi)有命中本機(jī)上的服務(wù)端
???return?super.doSelect(invocation,?providerInfos);
}
當(dāng)然,也需要看業(yè)務(wù)和內(nèi)部服務(wù)路由的實(shí)際情況,比如在阿里的單元化部署下,需要根據(jù)用戶ID路由到對(duì)應(yīng)的zone進(jìn)行處理,如果還是優(yōu)先本機(jī),那就可能在操作數(shù)據(jù)庫(kù)的時(shí)候涉及到跨zone調(diào)用,比走遠(yuǎn)程rpc更加耗時(shí)。因此這種情況下就需要禁用本機(jī)優(yōu)先策略。1.4延遲暴露
很多時(shí)候,我們的服務(wù)需要依賴一些其他內(nèi)容才可以正常提供服務(wù),比如緩存預(yù)熱、線程池預(yù)熱等等,所以,在服務(wù)真正就緒之后再注冊(cè)到配置中心是很有必要的。//服務(wù)注冊(cè)之前,先延遲
public?void?export()?{
????//?根據(jù)配置延遲加載
????if?(providerConfig.getDelay()?>?0)?{?
????????Thread?thread?=?factory.newThread(new?Runnable()?{
?????????@Override
?????????public?void?run()?{
?????????????try?{
??????????????????Thread.sleep(providerConfig.getDelay());
?????????????}?catch?(Throwable?ignore)?{?
?????????????}
??????????????//真正的服務(wù)注冊(cè)邏輯
??????????????doExport();
?????????}
??????});
??????thread.start();
???}?else?{
???????doExport();
???}
}
1.5粘滯連接
問(wèn):?我們需要每次都進(jìn)行路由尋址和負(fù)載均衡來(lái)確定服務(wù)地址么?答:?大部分情況是有利的,不過(guò)有些特殊的場(chǎng)景,更希望多次請(qǐng)求連接到同一臺(tái)服務(wù)器。比如,有狀態(tài)的服務(wù)(很多帶數(shù)據(jù)功能的服務(wù)都是有狀態(tài)的,比如很久之前的帶登陸session的Tomcat服務(wù)、存儲(chǔ)集群服務(wù)等),其實(shí)希望每次請(qǐng)求都連接到相同的服務(wù)器。這就用到了粘滯連接功能。
protected?ProviderInfo?select(...)throws?SofaRpcException?{
????//?判斷isSticky?粘滯連接配置
????if?(consumerConfig.isSticky())?{
????????//如果最后一次使用的provider不為空,則使用
????????if?(lastProviderInfo?!=?null)?{
????????????ProviderInfo?providerInfo?=?lastProviderInfo;????????
????????????//獲取對(duì)應(yīng)連接
????????????ClientTransport?lastTransport?=?connectionHolder.getAvailableClientTransport(providerInfo);
????????????if?(lastTransport?!=?null?