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

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

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