這篇文章主要講解了“RPC核心知識(shí)點(diǎn)有哪些”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“RPC核心知識(shí)點(diǎn)有哪些”吧!
白云網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián),白云網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為白云上1000+提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站制作要多少錢,請(qǐng)找那個(gè)售后服務(wù)好的白云做網(wǎng)站的公司定做!
RPC 全稱是 Remote Procedure Call ,即遠(yuǎn)程過(guò)程調(diào)用,其對(duì)應(yīng)的是我們的本地調(diào)用。
遠(yuǎn)程其實(shí)指的就是需要網(wǎng)絡(luò)通信,可以理解為調(diào)用遠(yuǎn)程機(jī)器上的方法。
那可能有人說(shuō):我用 HTTP 調(diào)用不就是遠(yuǎn)程調(diào)用了,那不也叫 RPC 了?
不是的,RPC 的目的是:讓我們調(diào)用遠(yuǎn)程方法像調(diào)用本地方法一樣無(wú)差別。
來(lái)看下代碼就很清晰,比如本來(lái)沒(méi)有拆分服務(wù)都是本地調(diào)用的時(shí)候方法是這樣寫的:
public String getSth(String str) { return yesService.get(str); }
如果 yesSerivce 被拆分出去,此時(shí)需要遠(yuǎn)程調(diào)用了,如果用 HTTP 方式,可能就是:
public String getSth(String str) { RequestParam param = new RequestParam(); ...... return HttpClient.get(url, param,.....); }
此時(shí)需要關(guān)心遠(yuǎn)程服務(wù)的地址,還需要組裝請(qǐng)求等等,而如果采用 RPC 調(diào)用那就是:
public String getSth(String str) { // 看起來(lái)和之前調(diào)用沒(méi)差?哈哈沒(méi)唬你, // 具體的實(shí)現(xiàn)已經(jīng)搬到另一個(gè)服務(wù)上了,這里只有接口。 // 看完下面就知道了。 return yesService.get(str); }
所以說(shuō) RPC 其實(shí)就是用來(lái)屏蔽遠(yuǎn)程調(diào)用網(wǎng)絡(luò)相關(guān)的細(xì)節(jié),使得遠(yuǎn)程調(diào)用和本地調(diào)用使用一致,讓開(kāi)發(fā)的效率更高。
在了解了 RPC 的作用之后,我們來(lái)看看 RPC 調(diào)用需要經(jīng)歷哪些步驟。
按上面的例子來(lái)說(shuō),yesService 服務(wù)實(shí)現(xiàn)被移到了遠(yuǎn)程服務(wù)上,本地沒(méi)有具體的實(shí)現(xiàn)只有一個(gè)接口。
那這時(shí)候我們需要調(diào)用 yesService.get(str)
,該怎么辦呢?
我們所要做的就是把傳入的參數(shù)和調(diào)用的接口全限定名通過(guò)網(wǎng)絡(luò)通信告知到遠(yuǎn)程服務(wù)那里。
然后遠(yuǎn)程服務(wù)接收到參數(shù)和接口全限定名就能選中具體的實(shí)現(xiàn)并進(jìn)行調(diào)用。
業(yè)務(wù)處理完之后再通過(guò)網(wǎng)絡(luò)返回結(jié)果,這就搞定了!
上面的操作這些就是由yesService.get(str)
觸發(fā)的。
不過(guò)我們知道 yesService 就是一個(gè)接口,沒(méi)有實(shí)現(xiàn)的,所以這些操作是怎么來(lái)的?
是通過(guò)動(dòng)態(tài)代理來(lái)的。
RPC 會(huì)給接口生成一個(gè)代理類,所以我們調(diào)用這個(gè)接口實(shí)際調(diào)用的是動(dòng)態(tài)生成的代理類,由代理類來(lái)觸發(fā)遠(yuǎn)程調(diào)用,這樣我們調(diào)用遠(yuǎn)程接口就無(wú)感知了。
動(dòng)態(tài)代理想必大家都比較熟悉,最常見(jiàn)的就是 Spring 的 AOP 了,涉及的有 JDK 動(dòng)態(tài)代理和 cglib。
在 Dubbo 中用的是 Javassist,至于為什么用這個(gè)其實(shí)梁飛大佬已經(jīng)寫了博客說(shuō)明了。
他當(dāng)時(shí)對(duì)比了 JDK 自帶的、ASM、CGLIB(基于ASM包裝)、Javassist。
經(jīng)過(guò)測(cè)試最終選用了 Javassist。
梁飛:最終決定使用JAVAASSIST的字節(jié)碼生成代理方式。雖然ASM稍快,但并沒(méi)有快一個(gè)數(shù)量級(jí),而JAVAASSIST的字節(jié)碼生成方式比ASM方便,JAVAASSIST只需用字符串拼接出Java源碼,便可生成相應(yīng)字節(jié)碼,而ASM需要手工寫字節(jié)碼。
可以看到選擇一個(gè)框架的時(shí)候性能是一方面,易用性也很關(guān)鍵。
說(shuō)回 RPC 。
現(xiàn)在我們知道動(dòng)態(tài)代理屏蔽了 RPC 調(diào)用的細(xì)節(jié),使得用戶無(wú)感知的調(diào)用遠(yuǎn)程服務(wù),那調(diào)用的細(xì)節(jié)有哪些呢?
像我們的請(qǐng)求參數(shù)都是對(duì)象,有時(shí)候是定義的 DTO ,有時(shí)候是 Map ,這些對(duì)象是無(wú)法直接在網(wǎng)絡(luò)中傳輸?shù)摹?/p>
你可以理解為對(duì)象是“立體”的,而網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)是“扁平”的,最終需要轉(zhuǎn)化成“扁平”的二進(jìn)制數(shù)據(jù)在網(wǎng)絡(luò)中傳輸。
你想想,各對(duì)象分配在內(nèi)存不同位置,各種引用,這看起來(lái)是不是有種立體的感覺(jué)?
最終都是要變成一段01組成的數(shù)字傳輸給對(duì)方,這種就01組成的數(shù)字看起來(lái)是不是很“扁平”?
把對(duì)象轉(zhuǎn)化成二進(jìn)制數(shù)據(jù)的過(guò)程稱為序列化,把二進(jìn)制數(shù)據(jù)轉(zhuǎn)化成對(duì)象的過(guò)程稱為反序列化。
當(dāng)然如何選擇序列化格式也很重要。
比如采用二進(jìn)制的序列化格式數(shù)據(jù)更加緊湊,采用 JSON 等文本型序列化格式可讀性更佳,排查問(wèn)題比較方便。
還有很多序列化選擇,一般需要綜合考慮通用性、性能、可讀性和兼容性。
具體本文就不分析了,之后再專門寫一篇分析各種序列化協(xié)議的。
剛才也提到了只有二進(jìn)制數(shù)據(jù)才能在網(wǎng)絡(luò)中傳輸,那一堆二進(jìn)制在底層看來(lái)是連起來(lái)的,它可不會(huì)管你哪些數(shù)據(jù)是哪個(gè)請(qǐng)求的。
但接收方得知道呀,不然就不能順利的把二進(jìn)制數(shù)據(jù)還原成對(duì)應(yīng)的一個(gè)個(gè)請(qǐng)求了。
于是就需要定義一個(gè)協(xié)議,來(lái)約定一些規(guī)范,制定一些邊界使得二進(jìn)制數(shù)據(jù)可以被還原。
比如下面一串?dāng)?shù)字按照不同位數(shù)來(lái)識(shí)別得到的結(jié)果是不同的。
所以協(xié)議其實(shí)就定義了到底如何構(gòu)造和解析這些二進(jìn)制數(shù)據(jù)。
我們的參數(shù)肯定比上面的復(fù)雜,因?yàn)閰?shù)值長(zhǎng)度是不定的,而且協(xié)議常常伴隨著升級(jí)而擴(kuò)展,畢竟有時(shí)候需要加一些新特性,那么協(xié)議就得變了。
一般 RPC 協(xié)議都是采用協(xié)議頭+協(xié)議體的方式。
協(xié)議頭放一些元數(shù)據(jù),包括:魔法位、協(xié)議的版本、消息的類型、序列化方式、整體長(zhǎng)度、頭長(zhǎng)度、擴(kuò)展位等。
協(xié)議體就是放請(qǐng)求的數(shù)據(jù)了。
通過(guò)魔法位可以得知這是不是咱們約定的協(xié)議,比如魔法位固定叫 233 ,一看我們就知道這是 233 協(xié)議。
然后協(xié)議的版本是為了之后協(xié)議的升級(jí)。
從整體長(zhǎng)度和頭長(zhǎng)度我們就能知道這個(gè)請(qǐng)求到底有多少位,前面多少位是頭,剩下的都是協(xié)議體,這樣就能識(shí)別出來(lái),擴(kuò)展位就是留著日后擴(kuò)展備用。
貼一下 Dubbo 協(xié)議:
可以看到有 Magic 位,請(qǐng)求 ID, 數(shù)據(jù)長(zhǎng)度等等。
組裝好數(shù)據(jù)就等著發(fā)送了,這時(shí)候就涉及網(wǎng)絡(luò)傳輸了。
網(wǎng)絡(luò)通信那就離不開(kāi)網(wǎng)絡(luò) IO 模型了。
網(wǎng)絡(luò) IO 分為這四種模型,具體以后單獨(dú)寫文章分析,這篇就不展開(kāi)了。
一般而言我們用的都是 IO 多路復(fù)用,因?yàn)榇蟛糠?RPC 調(diào)用場(chǎng)景都是高并發(fā)調(diào)用,IO 復(fù)用可以利用較少的線程 hold 住很多請(qǐng)求。
一般 RPC 框架會(huì)使用已經(jīng)造好的輪子來(lái)作為底層通信框架。
例如 Java 語(yǔ)言的都會(huì)用 Netty ,人家已經(jīng)封裝的很好了,也做了很多優(yōu)化,拿來(lái)即用,便捷高效。
RPC 通信的基礎(chǔ)流程已經(jīng)講完了,看下圖:
響應(yīng)返回就沒(méi)畫了,反正就是倒著來(lái)。
我再用一段話來(lái)總結(jié)一下:
服務(wù)調(diào)用方,面向接口編程,利用動(dòng)態(tài)代理屏蔽底層調(diào)用細(xì)節(jié)將請(qǐng)求參數(shù)、接口等數(shù)據(jù)組合起來(lái)并通過(guò)序列化轉(zhuǎn)化為二進(jìn)制數(shù)據(jù),再通過(guò) RPC 協(xié)議的封裝利用網(wǎng)絡(luò)傳輸?shù)椒?wù)提供方。
服務(wù)提供方根據(jù)約定的協(xié)議解析出請(qǐng)求數(shù)據(jù),然后反序列化得到參數(shù),找到具體調(diào)用的接口,然后執(zhí)行具體實(shí)現(xiàn),再返回結(jié)果。
這里面還有很多細(xì)節(jié)。
比如請(qǐng)求都是異步的,所以每個(gè)請(qǐng)求會(huì)有唯一 ID,返回結(jié)果會(huì)帶上對(duì)應(yīng)的 ID, 這樣調(diào)用方就能通過(guò) ID 找到對(duì)應(yīng)的請(qǐng)求塞入相應(yīng)的結(jié)果。
有人會(huì)問(wèn)為什么要異步,那是為了提高吞吐。
當(dāng)然還有很多細(xì)節(jié),會(huì)在之后剖析 Dubbo 的時(shí)候提到,結(jié)合實(shí)際中間件體會(huì)才會(huì)更深。
以上提到的只是 RPC 的基礎(chǔ)流程,這對(duì)于工業(yè)級(jí)別的使用是遠(yuǎn)遠(yuǎn)不夠的。
生產(chǎn)環(huán)境中的服務(wù)提供者都是集群部署的,所以有多個(gè)提供者,而且還會(huì)隨著大促等流量情況動(dòng)態(tài)增減機(jī)器。
因此需要注冊(cè)中心,作為服務(wù)的發(fā)現(xiàn)。
調(diào)用者可以通過(guò)注冊(cè)中心得知服務(wù)提供者們的 IP 地址等元信息,進(jìn)行調(diào)用。
調(diào)用者也能通過(guò)注冊(cè)中心得知服務(wù)提供者下線。
還需要有路由分組策略,調(diào)用者根據(jù)下發(fā)的路由信息選擇對(duì)應(yīng)的服務(wù)提供者,能實(shí)現(xiàn)分組調(diào)用、灰度發(fā)布、流量隔離等功能。
還需要有負(fù)載均衡策略,一般經(jīng)過(guò)路由過(guò)濾之后還是有多個(gè)服務(wù)提供者可以選擇,通過(guò)負(fù)載均衡策略來(lái)達(dá)到流量均衡。
當(dāng)然還需要有異常重試,畢竟網(wǎng)絡(luò)是不穩(wěn)定的,而且有時(shí)候某個(gè)服務(wù)提供者也可能出點(diǎn)問(wèn)題,所以一次調(diào)用出錯(cuò)進(jìn)行重試,較少業(yè)務(wù)的損耗。
還需要限流熔斷,限流是因?yàn)榉?wù)提供者不知道會(huì)接入多少調(diào)用者,也不清楚每個(gè)調(diào)用者的調(diào)用量,所以需要衡量一下自身服務(wù)的承受值來(lái)進(jìn)行限流,防止服務(wù)崩潰。
而熔斷是為了防止下游服務(wù)故障導(dǎo)致自身服務(wù)調(diào)用超時(shí)阻塞堆積而崩潰,特別是調(diào)用鏈很長(zhǎng)的那種,影響很大。
比如A=>B=>C=>D=>E,然后 E 出了故障,你看ABCD四個(gè)服務(wù)就傻等著,慢慢的資源就占滿了就崩了,全崩。
大致就是以上提到的幾點(diǎn),不過(guò)還能細(xì)化,比如負(fù)載均衡的各種策略、限流到底是限制總流量還是根據(jù)每個(gè)調(diào)用者指定限流量,還是上自適應(yīng)限流等等。
感謝各位的閱讀,以上就是“RPC核心知識(shí)點(diǎn)有哪些”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)RPC核心知識(shí)點(diǎn)有哪些這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!