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