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