關於rpc的整理和理解

glmapper發表於2018-03-11

架構演變

  • 所有的介面和服務均在同一個程式下

    關於rpc的整理和理解

  • 基於mvc的檢視與服務分離,但是實際上還是在一個應用系統中,只不過在功能層次上劃分的更加細緻

    關於rpc的整理和理解

  • 粒度更細,對於不同的功能服務進行切分,並進行單獨的部署

    關於rpc的整理和理解

  • 面向服務的架構,將應用程式的不同功能單元(稱為服務)通過這些服務之間定義良好的介面和契約聯絡起來

    關於rpc的整理和理解

  • 微服務

    此處不支援圖片展示,自行腦補!!!

隨著業務量和使用者量的增加,架構也是從單一系統走向分散式系統,我能想到的是,這種架構的演變主要解決的問題在於:

  • 通過業務模組的拆分,使得每個模組的職責更加清晰,但是模組的職責邊界的劃分往往也是很疼頭的事情。
  • 細緻的劃分使得專案在管理上面會更加方面,從程式碼的角度來說,開發和維護的成本也會降低,不會因為一個bug去跑整個專案了。
  • 提高了系統的容錯率,單一系統如果當機那就真的gg了,另外就是,單個環節出現問題也會導致專案無法正常執行(比如資料庫出問題了)。對於分散式系統來說,一般都會使用冗餘的方式來提高可用性,個人理解就是可以提供多個一樣的服務,它們之間可以進行切換。
  • 分散式系統帶來的問題一個是成本,硬體成本,運維成本都會增加。

rpc簡介及常用的rpc框架

隨著集中式架構向分散式架構的轉變,應用系統之間的服務呼叫與通訊問題成為了首要解決的需求。

而RPC 的主要目標就是為了讓構建分散式計算(應用)變得更加簡單,在提供強大的遠端呼叫能力時不損失本地呼叫的語義簡潔性。 為實現該目標,RPC 框架需提供一種透明呼叫機制讓使用者不必顯式的區分本地呼叫和遠端呼叫。

如下程式碼:

    @Autowired
	private GlRpcAgent glRpcAgent; //rpc代理
	/**
	 * @param param 此處約定引數以Map鍵值對的形式傳遞
	 */
	@Override
	public List<OrderInfo> queryOrdersByUserId(Map<String, Object> param) {
		//建立遠端呼叫代理(遠端服務的類的全限定名)
		OrderConsumeAgent orderConsumer=glRpcAgent.getAgent("com.glmapper.rpc.interface.OrderConsumeInterface");
		//通過代理獲取返回結果  此處getOrders為遠端伺服器上的com.glmapper.rpc.interface.OrderConsumeInterface介面中的方法,param為引數
		Map<String,Object> resultMap=(Map)orderConsumer.call("getOrders",param);
		//解析返回結果(遠端方法同樣以Map集合的方式放回)
		List<OrderInfo> orders = parseResultMap(resultMap);
		return orders;
	}
複製程式碼

為什麼要以全限定名來獲取呢,這個我們將會在後面來說。

什麼是RPC

In distributed computing a remote procedure call (RPC) is when a computer program causes a procedure (subroutine) to execute in another address space(commonly on another computer on a shared network), which is coded as if it were a normal (local) procedure call, without the programmer explicitly coding the details for the remote interaction. RPC 的全稱是 Remote Procedure Call 是一種程式間通訊方式。 它允許程式呼叫另一個程式上(通常是共享網路的另一臺機器上)的過程或函式,而不用程式設計師顯式編碼這個遠端呼叫的細節。即程式設計師無論是呼叫本地的還是遠端的函式,本質上編寫的呼叫程式碼基本相同

從定義中可以得知,RPC主要來解決三件事情:

  • 程式間通訊
  • 提供和本地方法呼叫一樣的呼叫機制
  • 遮蔽程式設計師對遠端呼叫的細節實現

首先是程式間的通訊問題,對於分散式環境,rpc能夠幫助我們解決不同伺服器之間的通訊及資料傳輸問題,即**做好方法呼叫到資料的轉換,然後藉助網路進行資料傳遞;**rpc客戶端向rpc服務端發起遠端服務呼叫,通過請求的封裝,引數的封裝,序列化、編碼、約定協議傳輸、解析請求、處理請求、封裝返回訊息資料、在進行返回資料的序列化、編碼、在通過網路返回給客戶端。再者是提供和本地方法呼叫一樣的呼叫機制,為什麼這麼說,對於業務系統來說,我們更多的關注點在於如何解決實際的業務需求問題,而不想花更多的時間和心思在諸如上述過程中關於網路傳輸及編解碼過程,因此對於rpc來說,需要將這些編解碼、協議約定、網路傳輸等進行一個整體的封裝,然後只向業務系統提供最簡單的呼叫方式。最後一個遮蔽程式設計師對遠端呼叫的細節實現,其實也就是第二點中提到的那些功能的封裝,我們不用去關係rpc到底是如何實現的,也不用關心它是如何運作的,對於業務開發人員來說,通過約定的方式進行類似於本地方法呼叫的形式來呼叫遠端服務介面就可以了。 那麼如何實現透明化的遠端呼叫呢? 什麼樣的內部封裝才能讓我們覺得像以本地呼叫方式呼叫遠端服務呢? 對於java來說就是使用代理。java代理有兩種方式:1) jdk 動態代理(介面代理);2)cglib代理(子類代理)。儘管位元組碼生成方式實現的代理更為強大和高效,但程式碼不易維護,大部分公司實現RPC框架時還是選擇動態代理方式。這部分也將會在後續的章節中展開來說。

RPC基本原理

上面說到,rpc需要對一些遠端呼叫的內部實現進行封裝。我們說到有以下幾個點:

  • 序列化
  • 編解碼
  • 協議
  • 網路

從發起遠端呼叫到接收到資料返回結果,大致過程是:

1)服務消費方(client)呼叫以本地呼叫方式呼叫服務; 2)client stub接收到呼叫後負責將方法、引數等組裝成能夠進行網路傳輸的訊息體; 3)client stub找到服務地址,並將訊息傳送到服務端; 4)server stub收到訊息後進行解碼; 5)server stub根據解碼結果呼叫本地的服務; 6)本地服務執行並將結果返回給server stub; 7)server stub將返回結果打包成訊息併傳送至消費方; 8)client stub接收到訊息,並進行解碼; 9)服務消費方得到最終結果。

那麼rpc就相當於將step2-step8的步驟進行了封裝。下面借用一張網上的圖片來幫助我們理解這個過程。

關於rpc的整理和理解

RPC模型

對於上圖,我們進行進一步的拆解得到(來自網路):

關於rpc的整理和理解
RPC 服務端通過 RpcServer 去暴露服務介面,而客戶端通過 RpcClient 去獲取服務介面。客戶端像呼叫本地方法一樣去呼叫遠端介面方法,RPC 框架提供介面的代理實現,實際的呼叫將委託給代理 RpcProxy。代理封裝呼叫資訊並將呼叫轉交給 RpcInvoker 去實際執行。在客戶端的 RpcInvoker 通過聯結器 RpcConnector 去維持與服務端的通道 RpcChannel,並使用 RpcProtocol 執行協議編碼(encode)並將編碼後的請求訊息通過通道傳送給服務端。RPC 服務端接收器 RpcAcceptor接收客戶端的呼叫請求,同樣使用 RpcProtocol 執行協議解碼(decode)。 解碼後的呼叫資訊傳遞給 RpcProcessor 去控制處理呼叫過程,最後再委託呼叫給 RpcInvoker 去實際執行並返回撥用結果。

通過上述分析可知,這裡麵包括以下核心元件:

  • 用於暴露服務介面的RpcServer
  • 用於發現服務介面的RpcClient
  • 遠端介面的代理實現RpcProxy
  • 負責協議編解碼的RpcProtocol(實際的rpc框架中一般會提供多種不同的實現)
  • 網路聯結器 (之前看過一篇文章說9個元件,對於我們們這個來說,部分模組可以整合在client和server中)

常見的RPC框架

目前常見的分散式RPC框架有以下幾個:

  • dubbo 阿里巴巴公司開源的一個Java高效能優秀的服務框架,使得應用可通過高效能的 RPC 實現服務的輸出和輸入功能,可以和 Spring框架無縫整合
  • motan 新浪微博開源的一個Java 框架。它誕生的比較晚,起於2013年,2016年5月開源。Motan 在微博平臺中已經廣泛應用,每天為數百個服務完成近千億次的呼叫。
  • rpcx Go語言生態圈的Dubbo, 比Dubbo更輕量,實現了Dubbo的許多特性,藉助於Go語言優秀的併發特性和簡潔語法,可以使用較少的程式碼實現分散式的RPC服務。
  • gRPC Google開發的高效能、通用的開源RPC框架,主要面向移動應用開發並基於HTTP/2協議標準而設計,基於ProtoBuf(Protocol Buffers)序列化協議開發,且支援眾多開發語言。本身它不是分散式的,所以要實現上面的框架的功能需要進一步的開發。
  • thrift Apache的一個跨語言的高效能的服務框架

RPC與MQ

MQ(message queue)訊息佇列,從某種程度上來說,同樣可以實現RPC的功能。從功能特點上來說,MQ可以把訊息儲存,而RPC不行。關於MQ和RPC做了以下簡單的對比,如下圖所示:

關於rpc的整理和理解

總結

本文對RPC的基本原理、特點以及基本元件進行了簡單的說明,讓我們可以對RPC有一個基本的瞭解。關於常見的RPC框架也做了基本認識,對於這些優秀的框架,我們在實現我們自己RPC時可以借鑑一下這些架構裡的一些模式以及技術。最後說明了下為什麼我們會在分散式架構中要使用RPC而不是MQ,對於MQ來說,在處理同步呼叫無法滿足實際的生產需求,而RPC才更加適合分散式應用的實際需要。

相關文章