一文搞懂RPC原理


RPC原理解析

什么是RPC

RPC(Remote Procedure Call Protocol)——遠程過程調用協議,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。RPC協議假定某些傳輸協議的存在,如TCP/IP或UDP,為通信程序之間攜帶信息數據。RPC將原來的本地調用轉變為調用遠端的服務器上的方法,給系統的處理能力和吞吐量帶來了近似於無限制提升的可能。在OSI網絡通信模型中,RPC跨域了傳輸層和應用層。RPC使得開發包括網絡分布式多程序在內的應用程序更加容易。

RPC架構

一個完整的RPC架構里面包含了四個核心的組件,分別是Client,Client Stub,Server以及Server Stub,這個Stub可以理解為存根。

客戶端(Client):服務的調用方。
客戶端存根(Client Stub):存放服務端的地址消息,再將客戶端的請求參數打包成網絡消息,然后通過網絡遠程發送給服務方。
服務端(Server):真正的服務提供者。
服務端存根(Server Stub):接收客戶端發送過來的消息,將消息解包,並調用本地的方法。

RPC調用過程


(1) 客戶端(client)以本地調用方式(即以接口的方式)調用服務;
(2) 客戶端存根(client stub)接收到調用后,負責將方法、參數等組裝成能夠進行網絡傳輸的消息體(將消息體對象序列化為二進制);
(3) 客戶端通過sockets將消息發送到服務端;
(4) 服務端存根( server stub)收到消息后進行解碼(將消息對象反序列化);
(5) 服務端存根( server stub)根據解碼結果調用本地的服務;
(6) 本地服務執行並將結果返回給服務端存根( server stub);
(7) 服務端存根( server stub)將返回結果打包成消息(將結果消息對象序列化);
(8) 服務端(server)通過sockets將消息發送到客戶端;
(9) 客戶端存根(client stub)接收到結果消息,並進行解碼(將結果消息發序列化);
(10) 客戶端(client)得到最終結果。
RPC的目標是要把2、3、4、7、8、9這些步驟都封裝起來。
注意:無論是何種類型的數據,最終都需要轉換成二進制流在網絡上進行傳輸,數據的發送方需要將對象轉換為二進制流,而數據的接收方則需要把二進制流再恢復為對象。

RPC框架

RPC是分布式系統/應用中應用最為廣泛的一種協議,最常見的RPC框架為grpc、Dubble等,下面以Dubble為示例,分析下Dubble框架中RPC的具體使用和實現原理;

Dubble中RPC實現分析

	//客戶端
	@Component
	public class HelloClient {

		@Reference // dubbo 注解
		private HelloService helloService;

		public String hello() {
			return helloService.hello("World");
		}
	}

	// 服務端
	@Service // dubbo 注解
	@Component
	public class HelloServiceImpl implements HelloService {

		@Override
		public String hello(String name) {
			return "Hello " + name;
		}
	}

示例分析

在客戶端,我們可以通過 @Reference 注解,獲得一個實現了 HelloServicer 這個接口的對象,我們的業務代碼只要調用這個對象的方法,就可以獲得結果。對於客戶端代碼來說,調用就是 helloService 這個本地對象,但實際上,真正的服務是在遠程的服務端進程中實現的。

再來看服務端,在服務端我們的實現類 HelloServiceImpl,實現了 HelloService 這個接口。然后,我們通過 @Service 這個注解(注意,這個 @Service 是 Dubbo 提供的注解,不是 Spring 提供的同名注解),在 Dubbo 框架中注冊了這個實現類 HelloServiceImpl。在服務端,我們只是提供了接口 HelloService 的實現,並沒有任何遠程調用的實現代碼。

對於業務代碼來說,無論是客戶端還是服務端,除了增加了兩個注解以外,和實現一個進程內調用沒有任何區別。Dubbo 看起來就像把服務端進程中的實現類“映射”到了客戶端進程中一樣。接下來我們一起來看一下,Dubbo 這類 RPC 框架是如何來實現調用遠程服務的。

實現分析

在客戶端,業務代碼得到的 HelloService 這個接口的實例,並不是我們在服務端提供的真正的實現類 HelloServiceImpl 的一個實例。它實際上是由 RPC 框架提供的一個代理類的實例。這個代理類有一個專屬的名稱,叫“樁(Stub)”。

在不同的 RPC 框架中,這個樁的生成方式並不一樣,有些是在編譯階段生成的,有些是在運行時動態生成的,這個和編程語言的語言特性是密切相關的,所以在不同的編程語言中有不同的實現,這部分很復雜,可以先不用過多關注。我們只需要知道這個樁它做了哪些事兒就可以了。

我們知道,HelloService 的樁,同樣要實現 HelloService 接口,客戶端在調用 HelloService 的 hello 方法時,實際上調用的是樁的 hello 方法,在這個樁的 hello 方法里面,它會構造一個請求,這個請求就是一段數據結構,請求中包含兩個重要的信息:

  1. 請求的服務名,在我們這個例子中,就是 HelloService#hello(String),也就是說,客戶端調用的是 HelloService 的 hello 方法;
  2. 請求的所有參數,在我們這個例子中,就只有一個參數 name, 它的值是“World”。
    然后,它會把這個請求發送給服務端,等待服務的響應。這個時候,請求到達了服務端,然后我們來看服務端是怎么處理這個請求的。

服務端的 RPC 框架收到這個請求之后,先把請求中的服務名解析出來,然后,根據這個服務名找一下,在服務端進程中,有沒有這個服務名對應的服務提供者。

在這個例子的服務端中,由於我們已經通過 @Service 注解向 RPC 框架注冊過 HelloService 的實現類,所以,RPC 框架在收到請求后,可以通過請求中的服務名找到 HelloService 真正的實現類 HelloServiceImpl。找到實現類之后,RPC 框架會調用這個實現類的 hello 方法,使用的參數值就是客戶端發送過來的參數值。服務端的 RPC 框架在獲得返回結果之后,再將結果封裝成響應,返回給客戶端。

客戶端 RPC 框架的樁收到服務端的響應之后,從響應中解析出返回值,返回給客戶端的調用方。這樣就完成了一次遠程調用。我把這個調用過程畫成一張圖放在下面,你可以對着這張圖再消化一下上面的流程。

在上面的這個調用流程中,客戶端是如何找到服務端的呢?在 RPC 框架中,這部分的實現原理和消息隊列的實現是完全一樣的,都是通過一個 NamingService來解決的。

在 RPC 框架中,這個 NamingService 一般稱為注冊中心。服務端的業務代碼在向 RPC 框架中注冊服務之后,RPC 框架就會把這個服務的名稱和地址發布到注冊中心上。客戶端的樁stud在調用服務端之前,會向注冊中心請求服務端的地址,請求的參數就是服務名稱,也就是我們上面例子中的方法簽名 HelloService#hello,注冊中心會返回提供這個服務的地址,然后客戶r55555767 端再去請求服務端。

有些 RPC 框架,比如 gRPC,是可以支持跨語言調用的。它的服務提供方和服務調用方是可以用不同的編程語言來實現的。比如,我們可以用 Python 編寫客戶端,用 Go 語言來編寫服務端,這兩種語言開發的服務端和客戶端仍然可以正常通信。這種支持跨語言調用的 RPC 框架的實現原理和普通的單語言的 RPC 框架並沒有什么本質的不同。

我們可以再回顧一下上面那張調用的流程圖,如果需要實現跨語言的調用,也就是說,圖中的客戶端進程和服務端進程是由兩種不同的編程語言開發的。其實,只要客戶端發出去的請求能被服務端正確解析,同樣,服務端返回的響應,客戶端也能正確解析,其他的步驟完全不用做任何改變,不就可以實現跨語言調用了嗎?

在客戶端和服務端,收發請求響應的工作都是 RPC 框架來實現的,所以,只要 RPC 框架保證在不同的編程語言中,使用相同的序列化協議,就可以實現跨語言的通信。另外,為了在不同的語言中能描述相同的服務定義,也就是我們上面例子中的 HelloService 接口,跨語言的 RPC 框架還需要提供一套描述服務的語言,稱為 IDL(Interface description language)。所有的服務都需要用 IDL 定義,再由 RPC 框架轉換為特定編程語言的接口或者抽象類。這樣,就可以實現跨語言調用了。

講到這里,RPC 框架的基本實現原理就很清楚了,可以看到,實現一個簡單的 RPC 框架並不是很難,這里面用到的絕大部分技術,包括:高性能網絡傳輸、序列化和反序列化、服務路由的發現方法等,與消息隊列實現原理中的內容基本類似。

總結

學習一個東西最好的方法就是動手實踐,后面文章中我們會手動實現一個RPC框架,並解析一下現存優秀的RPC框架


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM