自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java 從零開始手寫 RPC基于 Websocket 實現(xiàn)

開發(fā) 后端
RPC(Remote Procedure Call Protocol)--遠(yuǎn)程過程調(diào)用協(xié)議,它是一種通過網(wǎng)絡(luò)從遠(yuǎn)程計算機(jī)程序上請求服務(wù),而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議。

RPC

解決的問題

RPC 主要是為了解決的兩個問題:

(1)解決分布式系統(tǒng)中,服務(wù)之間的調(diào)用問題。

(2)遠(yuǎn)程調(diào)用時,要能夠像本地調(diào)用一樣方便,讓調(diào)用者感知不到遠(yuǎn)程調(diào)用的邏輯。

這一節(jié)我們來學(xué)習(xí)下如何基于 websocket 實現(xiàn)最簡單的 rpc 調(diào)用,后續(xù)會實現(xiàn)基于 netty4 的版本。

開源地址: https://github.com/houbb/rpc

完整流程

java 從零開始手寫 RPC (01) 基于 websocket 實現(xiàn)

其中左邊的Client,對應(yīng)的就是前面的Service A,而右邊的Server,對應(yīng)的則是Service B。

下面一步一步詳細(xì)解釋一下。

(1)Service A的應(yīng)用層代碼中,調(diào)用了Calculator的一個實現(xiàn)類的add方法,希望執(zhí)行一個加法運算;

(2)這個Calculator實現(xiàn)類,內(nèi)部并不是直接實現(xiàn)計算器的加減乘除邏輯,而是通過遠(yuǎn)程調(diào)用Service B的RPC接口,來獲取運算結(jié)果,因此稱之為Stub;

(3)Stub怎么和Service B建立遠(yuǎn)程通訊呢?這時候就要用到遠(yuǎn)程通訊工具了,也就是圖中的Run-time Library,這個工具將幫你實現(xiàn)遠(yuǎn)程通訊的功能,比如Java的Socket,就是這樣一個庫,當(dāng)然,你也可以用基于Http協(xié)議的HttpClient,或者其他通訊工具類,都可以,RPC并沒有規(guī)定說你要用何種協(xié)議進(jìn)行通訊;

(4)Stub通過調(diào)用通訊工具提供的方法,和Service B建立起了通訊,然后將請求數(shù)據(jù)發(fā)給Service B。需要注意的是,由于底層的網(wǎng)絡(luò)通訊是基于二進(jìn)制格式的,因此這里Stub傳給通訊工具類的數(shù)據(jù)也必須是二進(jìn)制,比如calculator.add(1,2),你必須把參數(shù)值1和2放到一個Request對象里頭(這個Request對象當(dāng)然不只這些信息,還包括要調(diào)用哪個服務(wù)的哪個RPC接口等其他信息),然后序列化為二進(jìn)制,再傳給通訊工具類,這一點也將在下面的代碼實現(xiàn)中體現(xiàn);

(5)二進(jìn)制的數(shù)據(jù)傳到Service B這一邊了,Service B當(dāng)然也有自己的通訊工具,通過這個通訊工具接收二進(jìn)制的請求;

(6)既然數(shù)據(jù)是二進(jìn)制的,那么自然要進(jìn)行反序列化了,將二進(jìn)制的數(shù)據(jù)反序列化為請求對象,然后將這個請求對象交給Service B的Stub處理;

(7)和之前的Service A的Stub一樣,這里的Stub也同樣是個“假玩意”,它所負(fù)責(zé)的,只是去解析請求對象,知道調(diào)用方要調(diào)的是哪個RPC接口,傳進(jìn)來的參數(shù)又是什么,然后再把這些參數(shù)傳給對應(yīng)的RPC接口,也就是Calculator的實際實現(xiàn)類去執(zhí)行。很明顯,如果是Java,那這里肯定用到了反射。

(8)RPC接口執(zhí)行完畢,返回執(zhí)行結(jié)果,現(xiàn)在輪到Service B要把數(shù)據(jù)發(fā)給Service A了,怎么發(fā)?一樣的道理,一樣的流程,只是現(xiàn)在Service B變成了Client,Service A變成了Server而已:Service B反序列化執(zhí)行結(jié)果->傳輸給Service A->Service A反序列化執(zhí)行結(jié)果 -> 將結(jié)果返回給Application,完畢。

簡單實現(xiàn)

假設(shè)服務(wù) A,想調(diào)用服務(wù) B 的一個方法。

因為不在同一個內(nèi)存中,無法直接使用。如何可以實現(xiàn)類似 Dubbo 的功能呢?

這里不需要使用 HTTP 級別的通信,使用 TCP 協(xié)議即可。

common

公用模塊,定義通用對象。

  • Rpc 常量
  1. public interface RpcConstant { 
  2.  
  3.  
  4.     /** 
  5.      * 地址 
  6.      */ 
  7.     String ADDRESS = "127.0.0.1"
  8.  
  9.  
  10.     /** 
  11.      * 端口號 
  12.      */ 
  13.     int PORT = 12345; 
  14.  
  15.  
  • 請求入?yún)?/li>
  1. public class RpcCalculateRequest implements Serializable { 
  2.  
  3.  
  4.     private static final long serialVersionUID = 6420751004355300996L; 
  5.  
  6.  
  7.     /** 
  8.      * 參數(shù)一 
  9.      */ 
  10.     private int one; 
  11.  
  12.  
  13.     /** 
  14.      * 參數(shù)二 
  15.      */ 
  16.     private int two; 
  17.  
  18.  
  19.     //getter & setter & toString() 
  • 服務(wù)接口
  1. public interface Calculator { 
  2.  
  3.  
  4.     /** 
  5.      * 計算加法 
  6.      * @param one 參數(shù)一 
  7.      * @param two 參數(shù)二 
  8.      * @return 返回結(jié)果 
  9.      */ 
  10.     int add(int one, int two); 
  11.  
  12.  

server

  • 服務(wù)接口的實現(xiàn)
  1. public class CalculatorImpl implements Calculator { 
  2.  
  3.  
  4.     @Override 
  5.     public int add(int one, int two) { 
  6.         return one + two; 
  7.     } 
  8.  
  9.  
  • 啟動服務(wù)
  1. public static void main(String[] args) throws IOException { 
  2.     Calculator calculator = new CalculatorImpl(); 
  3.     try (ServerSocket listener = new ServerSocket(RpcConstant.PORT)) { 
  4.         System.out.println("Server 端啟動:" + RpcConstant.ADDRESS + ":" + RpcConstant.PORT); 
  5.         while (true) { 
  6.             try (Socket socket = listener.accept()) { 
  7.                 // 將請求反序列化 
  8.                 ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); 
  9.                 Object object = objectInputStream.readObject(); 
  10.                 System.out.println("Request is: " + object); 
  11.                 // 調(diào)用服務(wù) 
  12.                 int result = 0; 
  13.                 if (object instanceof RpcCalculateRequest) { 
  14.                     RpcCalculateRequest calculateRpcRequest = (RpcCalculateRequest) object; 
  15.                     result = calculator.add(calculateRpcRequest.getOne(), calculateRpcRequest.getTwo()); 
  16.                 } 
  17.                 // 返回結(jié)果 
  18.                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); 
  19.                 objectOutputStream.writeObject(result); 
  20.             } catch (Exception e) { 
  21.                 e.printStackTrace(); 
  22.             } 
  23.         } 
  24.     } 

啟動日志:

  1. Server 端啟動:127.0.0.1:12345 

client

•客戶端調(diào)用

  1. public static void main(String[] args) { 
  2.     Calculator calculator = new CalculatorProxy(); 
  3.     int result = calculator.add(1, 2); 
  4.     System.out.println(result); 
  • 計算的代理類
  1. public class CalculatorProxy implements Calculator { 
  2.  
  3.  
  4.     @Override 
  5.     public int add(int one, int two) { 
  6.         try { 
  7.             Socket socket = new Socket(RpcConstant.ADDRESS, RpcConstant.PORT); 
  8.  
  9.  
  10.             // 將請求序列化 
  11.             RpcCalculateRequest calculateRpcRequest = new RpcCalculateRequest(one, two); 
  12.             ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); 
  13.  
  14.  
  15.             // 將請求發(fā)給服務(wù)提供方 
  16.             objectOutputStream.writeObject(calculateRpcRequest); 
  17.  
  18.  
  19.             // 將響應(yīng)體反序列化 
  20.             ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); 
  21.             Object response = objectInputStream.readObject(); 
  22.  
  23.  
  24.             if (response instanceof Integer) { 
  25.                 return (Integer) response; 
  26.             } else { 
  27.                 throw new RuntimeException(); 
  28.             } 
  29.         } catch (IOException | ClassNotFoundException e) { 
  30.             throw new RuntimeException(e); 
  31.         } 
  32.     } 
  • 調(diào)用日志

client 端

server 端

  1. Server 端啟動:127.0.0.1:12345 
  2. Request is: RpcCalculateRequest{one=1, two=2} 

 

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2021-10-20 08:05:18

Java 序列化 Java 基礎(chǔ)

2021-10-29 08:07:30

Java timeout Java 基礎(chǔ)

2021-10-19 08:58:48

Java 語言 Java 基礎(chǔ)

2021-10-21 08:21:10

Java Reflect Java 基礎(chǔ)

2021-10-14 08:39:17

Java Netty Java 基礎(chǔ)

2019-09-23 19:30:27

reduxreact.js前端

2021-10-27 08:10:15

Java 客戶端 Java 基礎(chǔ)

2015-11-17 16:11:07

Code Review

2018-04-18 07:01:59

Docker容器虛擬機(jī)

2019-01-18 12:39:45

云計算PaaS公有云

2020-07-02 15:32:23

Kubernetes容器架構(gòu)

2024-12-06 17:02:26

2010-05-26 17:35:08

配置Xcode SVN

2018-09-14 17:16:22

云計算軟件計算機(jī)網(wǎng)絡(luò)

2024-05-15 14:29:45

2023-11-09 23:45:01

Pytorch目標(biāo)檢測

2021-03-16 11:30:33

2015-05-06 09:36:05

Java語言從零開始學(xué)習(xí)

2024-11-28 10:35:47

2024-04-10 07:48:41

搜索引擎場景
點贊
收藏

51CTO技術(shù)棧公眾號