【RPC系列】1、聊一聊Rpc實現流程

RPC(Remote Procedure Call,遠程過程調用)是建立在Socket之上的一種多進程間的通信機制。

可以自動處理通信協議、對象序列化、網絡傳輸等複雜細節。

1、先了解Socket通信,類似聊天工具

客戶端:

//綁定連接主機和端口
Socket client = new Socket("127.0.0.1", 9999);
//創建一個輸出流,就用最簡單的
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()), 1000);
//獲取控制檯輸入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in, "utf-8"));
//往流裏面寫數據
while(true) {
	String str = br.readLine();
	bw.write(str);
	bw.write("\n");
	//關閉
	bw.flush();
	//退出聊天
	if("exit".equals(str)) {
		break;
	}
}
client.close();

服務端:

//開啓一個端口監控連接
ServerSocket server = new ServerSocket(9999);
//等待連接
Socket socket = server.accept();
//獲取輸入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//讀數據
while(true) {
	String str = br.readLine();
	if("exit".equals(str)) {
		break;
	}
	if(str != null) {
		System.out.println("<<<" + str);
	}
}
server.close();

2、熟悉的NIO框架

從以上簡單的socket通信來看,使用了字符串的傳輸,還會遇到字符串的編碼問題,要形成生產實用的框架還需要考慮:

(1)網絡異常情況。就是

(2)複雜數據傳輸編解碼問題。

(3)多客戶端連接以及服務端併發處理問題,連接複用,於是出現Nio模型的多路複用等。

(4)健康檢查(心跳)機制等。

(5)緩存機制提高讀寫效率

要學習TCP、UDP通信,還是需要惡補一下Socket的知識:

TCP通信:每個TCP Socket在內核中都有一個發送緩衝區和一個接收緩衝區,TCP的全雙工的工作模式及TCP的滑動窗口就是依賴於兩個獨立的Buffer(緩衝區)。接收緩衝區把數據緩存入內核,若應用進程一直沒有調用Socket的read方法,那麼數據就會一直被緩存在接收緩衝區。read所做的事就是把內核接收緩衝區的數據複製到應用層用戶的Buffer裏面send所做的事就是把應用層用戶的Buffer的數據複製到內核發送緩衝區

UDP通信:有接收緩衝區,沒有發送緩衝區,意思就是有數據就發,不管對方收到沒有。

TCP/IP滑動窗口和流量控制機制:想一下,如果Buffer滿了怎麼辦,因爲都無限制的去接收和發送。如果應用程序一直沒有read,那麼Buffer滿了之後就會通知對端TCP協議中的窗口關閉,保證TCP socket接收緩衝區不會溢出,這就是滑動窗口實現。因爲對方不允許發出超過窗口大小的數據,所以如果對方無視窗口大小而發出了超大數據,接收方就會選擇丟棄,就是TCP的流量控制。

瞭解了滑動窗口和流量控制的概念,就很容易理解“阻塞”的概念了,如果Buffer裏的數據是空的,那麼read的時候線程就會阻塞(等待)了,直到有數據進入接收緩衝區,發送也是一樣。如果待發送的數據大於Buffer發送緩衝區的大小了,那麼就會阻塞在write方法上,等待緩衝區的數據發送出去,再繼續發送下一段。

以上可以看出,每個socket的讀寫都要創建一個線程去處理,而且A/B兩方的讀寫效率會互相影響,A寫的效率慢,勢必造成B阻塞在讀上,多個socket連接就會出現大量線程阻塞在I/O等待上。

那如何做到“非阻塞”?無非就是將socket的讀寫與I/O線程解耦的問題。也就是NIO(No-Blocking I/O),於是就引入了事件的機制。有了事件,那就要想到需要有個事件驅動的總調度中心。我們可以認爲NIO底層就存在一個I/O調度線程,它可以輪詢掃描每個socket的緩衝區,當發現寫入緩衝區未滿的時候,就產生一個可寫事件,一次寫不完就等待下次可寫事件的通知,當發現接收緩衝區中有數據時就產生可讀事件。當然這都是程序去控制的,一定要注意輪詢的深度,切忌空輪詢。

以上可見阻塞和非阻塞的區別就是線程是否處於等待狀態中

關於Reactor模型在下節講訴。

3、大名鼎鼎RPC

首先清晰的理解下RPC和Socket在編程上的區別,以上基本瞭解了socket的編程和建立在socket通信的nio的模型,我們可以認爲是客戶端與服務端之間的通信連接編程,socket連接我們可以n vs 1,服務端綁定端口,客戶端只要提供Host和Port就可以進行連接了,那如果多服務端會出現什麼情況。

大名鼎鼎的RPC開發框架爲了針對分佈式系統開發誕生了。是由Sun公司提出的。爲了將上面的socket程序改寫成Rpc程序,我們需要將服務端和客戶端分離,即在socket接口外封裝一層Stub模塊,實現過程遠程調用所需要的通信功能,比如參數及調用結果的序列化功能,並通過網絡完成遠程傳輸。

看一下簡單的rpc實現流程:

整個rpc的調用過程如下:

(1)服務消費方(客戶端,消費者,訂閱者)-caller以本地調用方式調用服務。

(2)User-stub獲取到User的調用後負責將方法、參數等組裝成能夠在網絡中傳輸的消息體。

(3)RPC-runtime找到服務地址,並將消息發送到服務端。

(4)Callee收到消息後將請求交給Server-stub進行解碼。

(5)Server-stub解碼完之後根據服務指令交給Server去調用。

(6)Server執行完並將結果返回給Server-stub進行打包併發送至Caller。

(7)Caller接收到響應包後交給User-stub進行解碼並返回結果給User。

其中實現主要有三個模塊的技術要點:

  • 高性能網絡編程技術
  • 複雜結構消息體的序列化和反序列化技術
  • 動態代理編程技術

說到rpc就會說到微服務架構,一般而言,如果分佈式系統具有以下幾個特點,就可以稱之爲微服務架構:

  1. 任何一個服務都由多個獨立的進程提供服務,這些進程可以分佈在多臺物理機上,任何進程宕機,都不會影響系統提供服務
  2. 整個系統是由多個微服務有機組成的一個分佈式系統,換而言之,不是一個巨大的的單體應用。

目前主流的微服務架構分爲一下三類:

  • 基於傳統的高性能RPC技術和服務治理的微服務架構,代表爲ZeroC IceGrid
  • 以Http Rest爲通信機制的通用性微服務架構,最典型的爲Spring cloud
  • 基於容器技術,目標是部署在公有云平臺上的微服務架構基礎平臺,他們並沒有提供特定的RPC通信機制,只保證TCP的可達性,提供一個微服務平臺,任何分佈式系統都可以部署上面,只需要設定通信協議:REST、Thrift、gRPC或者自定義

本文參考Leader-us著《架構解密》、張亮等著《未來架構》

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章