1 什麼是RPC通信
RPC:Remote Procedure Call Protocol,指的是遠端過程調用協定,一般使用在分布式業務或者微服務架構風格中。
即一個節點通過網絡調用的方式來請求另一個節點提供的服務的過程,也可以簡單的了解為client通路server上提供的函數(像調用本地函數一樣,去調用一個遠端服務)。
2 RPC通信詳解
2.1 RCP角色和職能
在RPC架構中主要有三個角色:Provider、Consumer和Registry。如下圖所示:
節點角色說明,這邊看起來,跟其他的服務注冊與發現架構原理差不多(如 Eureka、Consul):
Service(provider): 暴露服務的服務提供方。
Client(consumer): 調用遠端服務的服務消費方。
Registry: 服務注冊與發現的注冊中心。
2.2 RPC調用流程
RPC(Remote Procedure Call)遠端過程調用,即一個節點通過網絡調用的方式來請求另一個節點提供的服務的過程,也可以簡單的了解為client通路server上提供的函數。
他的基本調用流程如下:
上面是一次完整的RPC調用流程(這邊指的是同步調用情況下),步驟順序如下:
- 用戶端(client)以本地調用方式(即以接口的方式)調用服務;
- 用戶端存根(client stub)接收到調用後,負責将方法、參數等組裝成能夠進行網絡傳輸的消息體(将消息體對象序列化為二進制 byte[]);
- 用戶端通過sockets将消息發送到服務端;
- 服務端存根( server stub)收到消息後進行解碼(将消息對象反序列化);
- 服務端存根( server stub)根據解碼結果調用本地的服務;
- 本地服務執行并将結果傳回給服務端存根( server stub);
- 服務端存根( server stub)将傳回結果打包成消息(将結果消息對象序列化);
- 服務端(server)通過sockets将消息發送到用戶端;
- 用戶端存根(client stub)接收到結果消息,并進行解碼(将結果消息反序列化);
- 用戶端(client)得到最終結果。
RPC的目标是要把2、3、4、7、8、9這些步驟都封裝起來。
無論是何種類型的資料,最終都需要轉換成二進制流在網絡上進行傳輸,資料的發送方需要将對象轉換為二進制流,而資料的接收方則需要把二進制流再恢複為對象。
2.3 多服務RPC通信模型
了解對服務的調用和RPC模式的結果傳回,注意箭頭的指向的差別。
2.4 RPC通信使用到的技術
1、動态代理(Spring中重點了解下)
生成 client stub和server stub需要用到 Java 動态代理技術 ,我們可以使用JDK原生的動态代理機制,可以使用一些開源位元組碼工具架構 如:CgLib、Javassist等。
2、序列化
序列化:将Java對象轉換成byte[]的過程,也就是編碼的過程;
反序列化:将byte[]轉換成Java對象的過程;
3、NIO
目前很多RPC架構都直接基于netty這一IO通信架構,比如阿裡巴巴的HSF、dubbo,Hadoop Avro,推薦使用 Netty 作為底層通信架構。
4、服務注冊中心
可選技術:Redis 、Zookeeper,
一般的RPC架構會接入注冊中心來進行注冊與發現的管理,比如Dubb與Zookeeper 的完美結合。
是以實作RPC調用過程,結構分為三部分:client、grpc、server。
2.5 RPC通信與普通函數調用的差別
雖然RPC可以簡單的了解為client 去調用server 中的函數。但是RPC通信和直接的函數調用還是有很大的差別的:
函數尋址IP端口路由(NamingService + LoadBalancer)+函數路由記憶體指針
傳遞資料序列化後的資料流記憶體對象
調用方異常處理timeout、retry、curcuit breaker抛出Exception / 函數傳回固定異常辨別的資料
被調用方異常處理認證鑒權、過載保護入參檢查 / 執行異常捕獲和處理
問題定位分布式Trace、監控、日志中心日志記錄 / 斷點調試跟蹤
性能優化連接配接池、多路複用、線程池、輕量級線程、non-block IO 等編譯器優化(inline等)
内容項RPC調用本地調用
這裡可以看出rpc比函數調用複雜的多,比如:
- 函數尋址:你怎樣調到你想要的哪個函數?在本地調用中其實就是一個函數指針,但是在RPC場景下,你要找到這個函數其實非常複雜,一個服務一般有多個下遊執行個體,首先要選擇一個下遊執行個體,一般這個是由NamingService+LB來實作,到達對應的執行個體後,服務端還要解析請求體,找到函數名,然後做函數路由。
- 資料傳遞:在本地調用過程中其實就是傳遞一個指針或者值,在RPC場景下其實是通過網絡傳遞的,網絡上需要傳遞一個記憶體對象序列化之後的一個二進制網絡資料流,response回來的時候也需要經過一個反序列化的過程。
- 異常處理:本地調用的情況下,無非就是判斷下這個函數的傳回值或者有沒有抛一些異常,但是在RPC場景下就很複雜,比如網絡擁塞了,服務端處理慢了或者逾時,還有很多異常的情況,是以我們要做很多系統容錯的事情,比如:逾時、重試等政策來解決這些問題。本地調用的時候我隻需要檢查參數是否合法,但是在RPC的場景下我們要做一些類似認證鑒權,過載保護等政策,避免流量過大将server打挂。
- 問題定位:本地調用方法很多,比如:斷點調試,打本地日志。但是在RPC場景下,這些方法其實是行不通的,我們需要分布式tracing、監控和分布式日志中心來幫助我們定位問題。
- 性能優化:本地調用其實我們不用關心太多,因為編譯器會幫我們做一些列的優化,但是在RPC的場景下,就需要我們自己優化通信效率,常用的優化手段比如:連接配接池、多路複用、線程池等等很多方法,這些方法實作起來都非常的複雜。現在大家應該能了解RPC場景是非常複雜的
正因為有如此的複雜性,是以我們需要一個RPC架構來處理這些複雜的事情,讓RPC看起來就像本地調用一樣簡單。
2.6 RPC 架構調用流分析
2.6.1 RPC架構功能(簡單版本)
實作的過程:
- client初始化一個channel,監聽NamingService,從服務名字中解析出來服務真正的上遊執行個體位址
- 用戶端将請求的的資料進行序列化
- 上遊可能多個執行個體,需要LB去選擇一個下遊的IP+Port,選出來之後需要和上遊執行個體建立連接配接和發送請求
- 建立連接配接之後發送請求
- 服務端接着接受連接配接和接受資料,收到資料之後将二進制資料反序列化為一個記憶體對象request
- 然後再調用server的響應方法進行處理
- 服務端通過sockets将消息發送到用戶端;
- 用戶端接收到結果消息,并進行解碼(将結果消息反序列化)
2.6.2 RPC架構功能(複雜版本)
有些RPC架構不隻是處理通信相關的工作(如資料的序列化和反序列化,協定的解析/打包,資料的壓縮解壓縮,資料的加密和解密),還可以做很多微服務治理的工作。
比如Dubbo支援對服務的治理,包括 服務注冊與發現、故障注入、逾時重試、負載均衡、連接配接管理和健康檢查等。除此之外,服務端還有認證鑒權、并發流量限制、函數路由、協定适配和參數校驗等等複雜的政策。
是以一個成熟的RPC架構也可以是一個非常複雜全面的分布式系統,在一定程度上協助工程進行微服務建設。
2.7 主流RPC 架構對比
公司AliGoogleBaiduFaceBook
通訊協定tcp/httphttp2多種協定tcp/http
序列化協定可擴充protobufprotobuf/json/mcpack可擴充
開發語言Java跨語言C++ / Java跨語言
主要特點服務治理、擴充性跨語言、性能高性能、擴充性跨語言
github star
36.9K33.5K12.9K8.9K
對比項DubbogRPCbrpcThrift
2.8 與RESTful API 的差別
RPC 主要用于公司内部的服務調用,性能消耗低,傳輸效率高,實作複雜。
HTTP 主要用于對外的異構環境,浏覽器接口調用,App 接口調用,第三方接口調用等。
RPC 使用場景(大型的網站,内部子系統較多、接口非常多的情況下适合使用 RPC):
- 長連結。不必每次通信都要像 HTTP 一樣去 3 次握手,減少了網絡開銷。
- 注冊釋出機制。RPC 架構一般都有注冊中心,有豐富的監控管理;釋出、下線接口、動态擴充等,對調用方來說是無感覺、統一化的操作。
- 安全性,沒有暴露資源操作。
- 微服務支援。就是最近流行的服務化架構、服務化治理,RPC 架構是一個強力的支撐。
3 總結
通過本篇我們詳細學習了RPC的概念和原理,以及它能夠提供的能力。也對目前業内主流的RPC的架構有了一定的了解。後面一篇我們以Dobbo為例子,來學習下怎麼使用RPC架構來進行服務之間的通信。