自己實現一個簡單的RPC框架
RPC的全稱是Remote Procedure Call,它是一種程序間的通訊方式。允許像呼叫本地服務一樣呼叫遠端服務。
對於RPC的總結:
- 簡單:RPC概念的語義十分簡單和清晰,這樣建立分散式計算更容易。
- 高效:過程呼叫看起來十分簡單而且十分高效。
- 通用:在單機計算過程中往往是不同演算法和API,在跨程序呼叫最重要的是通用的通訊機制。
對於RPC框架實現的技術點總結如下:
- 遠端服務提供者需要一某種形式提供服務呼叫的相關資訊,包括但不限於服務介面的定義、資料結構,或者中間狀態服務定義的檔案。
- 遠端代理物件:服務呼叫者呼叫的服務實際上市遠端服務的代理。
- 通訊:RPC框架的與具體的通訊協議無關
- 序列化:遠端通訊需要將物件轉換成二進位制碼流進行傳輸,不同的序列化框架支援的資料型別,資料包大小,異常型別及效能等都不相同。
最簡單的RPC框架實現:
- 服務提供者,執行在服務端,負責服務介面定義和服務介面實現。
- 服務釋出者,執行在RPC服務端,負責將本地服務釋出成遠端服務,供其他消費者呼叫
- 本地服務代理,執行在RPC客戶端,通過代理呼叫遠端服務提供者,然後對結果進行封裝返回給本地消費者
下面是具體的實現,
定義EchoService
介面,程式碼如下
/**
* @author zhuowen_pan.
* @version 1.0.0
*/
public interface EchoService {
String sayHello(String name);
}
EchoServiceImpl
定義該介面的實現類
/**
* @author zhuowen_pan.
* @version 1.0.0
*/
public class EchoServiceImpl implements EchoService {
@Override
public String sayHello(String name) {
return name == null ? "hello nobody" : "hello " + name;
}
}
RPC服務端服務釋出者程式碼的實現
/**
* @author zhuowen_pan.
* @version 1.0.0
*/
public class RpcExporter {
private static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public static void exporter(String hostName, int port) throws Exception {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(hostName, port));
try {
while (true) {
executor.execute(new ExporterTasks(serverSocket.accept()));
}
} finally {
serverSocket.close();
}
}
private static class ExporterTasks implements Runnable {
Socket client = null;
public ExporterTasks(Socket client) {
this.client = client;
}
@Override
public void run() {
ObjectInputStream input = null;
ObjectOutputStream out = null;
try {
input = new ObjectInputStream(client.getInputStream());
String interfaceName = input.readUTF();
Class<?> service = Class.forName(interfaceName);
String methodName = input.readUTF();
Class<?>[] paramsTypes = (Class<?>[]) input.readObject();
Object[] args = (Object[]) input.readObject();
Method method = service.getMethod(methodName, paramsTypes);
Object result = method.invoke(service.newInstance(), args);
out = new ObjectOutputStream(client.getOutputStream());
out.writeObject(result);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (input != null) {
input.close();
}
if (client != null) {
client.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
服務釋出者的主要職責
- 作為服務端,監聽客戶端的TCP連結,接收到客戶端的餓連結之後封裝成Tsak交給執行緒池執行
- 將客戶端傳送的碼流反序列化成物件,反射呼叫服務的實現者,獲取執行結果
- 將結果反序列化,通過Socket傳送給客戶端
- 遠端服務呼叫結束後,釋放資源
RPC客戶端本地服務代理原始碼如下
/**
* @author zhuowen_pan.
* @version 1.0.0
*/
public class RpcImporter<S> {
public S importer(final Class<?> serviceClass, final InetSocketAddress address) {
return (S) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class<?>[]{serviceClass.getInterfaces()[0]}, (proxy, method, args) -> {
Socket socket = null;
ObjectOutputStream outputStream = null;
ObjectInputStream inputStream = null;
try {
socket = new Socket();
socket.connect(address);
outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeUTF(serviceClass.getName());
outputStream.writeUTF(method.getName());
outputStream.writeObject(method.getParameterTypes());
outputStream.writeObject(args);
inputStream = new ObjectInputStream(socket.getInputStream());
return inputStream.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(outputStream != null) {
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
if (socket != null) {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
});
}
}
本地服務的代理的功能如下
- 將本地介面呼叫轉換成JDK的動態代理,在動態代理中實現介面的呼叫
- 建立Socket客戶端,根據指定地址呼叫遠端服務的提供者
- 將遠端介面呼叫所需要的介面類,方法名,引數列表等編碼後傳送給服務者
- 同步阻塞等待服務端返回應答,獲取執行結果
測試程式碼
/**
* @author zhuowen_pan.
* @version 1.0.0
*/
public class Client {
public static void main(String[] args) {
new Thread(() -> {
try {
RpcExporter.exporter("127.0.0.1", 8088);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
RpcImporter<EchoService> importer = new RpcImporter<>();
EchoService echoService = importer.importer(EchoServiceImpl.class, new InetSocketAddress("127.0.0.1", 8088));
System.out.println(echoService.sayHello(peter));
}
}
執行結果
到這裡一個簡單RPC服務框架就實現了。
相關推薦
自己實現一個簡單的RPC框架
RPC的全稱是Remote Procedure Call,它是一種程序間的通訊方式。允許像呼叫本地服務一樣呼叫遠端服務。 對於RPC的總結: 簡單:RPC概念的語義十分簡單和清晰,這樣建立分散式計算更容易。 高效:過程呼叫看起來十分簡單而且十分高效。
一個簡單RPC框架是如何煉成的(IV)——實現RPC訊息的編解碼
之前我們制定了一個很簡單的RPC訊息 的格式,但是還遺留了兩個問題,上一篇解決掉了一個,還留下一個 我們並沒有實現相應的encode和decode方法,沒有基於可以跨裝置的字串傳輸,而是直接的記憶體變數傳遞。 現在的RPC request不支援帶引數的請求命令。如add(a,
一個簡單RPC框架是如何煉成的(III)——實現帶引數的RPC呼叫
上一篇,我們制定了一個很簡單的RPC訊息 的格式,但是還遺留了兩個問題 我們並沒有實現相應的encode和decode方法,沒有基於可以跨裝置的字串傳輸,而是直接的記憶體變數傳遞。 現在的RPC request不支援帶引數的請求命令。如add(a, b), 如何在RPC訊息中描述
[python] 理解metaclass並實現一個簡單ORM框架
lds asc into password 這樣的 內容 建行 ati 什麽 metaclass 除了使用type()動態創建類以外,要控制類的創建行為,還可以使用metaclass。 metaclass,直譯為元類,簡單的解釋就是: 當我們定義了類以後,就可以根據這個
一個簡單RPC框架是如何煉成的(V)——引入傳輸層
開局篇我們說了,RPC框架的四個核心內容 RPC資料的傳輸。 RPC訊息 協議 RPC服務註冊 RPC訊息處理 接下來處理資料傳輸。實際應用場景一般都是基於socket。socket程式碼比較多,使用起來也比較麻煩。而且具體的傳輸
一個簡單RPC框架是如何煉成的(II)——制定RPC訊息
開局篇我們說了,RPC框架的四個核心內容 RPC資料的傳輸。 RPC訊息 協議 RPC服務註冊 RPC訊息處理 下面,我們先看一個普通的過程呼叫 class Client(object): def __init__(sel
一個簡單RPC框架是如何煉成的(I)——開局篇
開場白,這是一個關於RPC的相關概念的普及篇系列,主要是通過一步步的調整,提煉出一個相對完整的RPC框架。 RPC(Remote Procedure Call Protocol)——遠端過程呼叫協議,基於C/S模型。網路上有一篇文章寫得不錯,可以去了解一下相關概念深入淺出RPC 這裡,直接使
自己實現一個簡單版的HashMap
public class MyHashMap { //預設初始化大小 16 private static final int DEFAULT_INITIAL_CAPACITY = 16; //預設負載因子
自己實現一個簡單ArrayList
廢話不多說直接上程式碼package com.zgs.utils; import java.util.ArrayList; import org.aspectj.weaver.patterns.ExactAnnotationFieldTypePattern; /**
如何實現一個分散式 RPC 框架
遠端過程呼叫(Remote Procedure Call,RPC)是一個計算機通訊協議。該協議允許運行於一臺計算機的程式呼叫另一臺計算機的子程式,而程式設計師無需額外地為這個互動作用程式設計。RPC的主要目標是讓構建分散式應用更加容易,在提供強大的遠端呼叫能力的同時不損失
python裝飾器,自己實現一個簡單的裝飾器
裝飾器演變過程 1.先來看個方法: def add(x,y): return x+y 其功能顯而易見,實現一個加法,boss覺得這個功能太單一,需要加些功能1,校驗(因為python是強型別語言,int 和str
從零開始實現一個IDL+RPC框架
一、RPC是什麼 在很久之前的單機時代,一臺電腦中跑著多個程序,程序之間沒有交流各幹各的,就這樣過了很多年。突然有一天有了新需求,
基於Netty和SpringBoot實現一個輕量級RPC框架-協議篇
基於Netty和SpringBoot實現一個輕量級RPC框架-協議篇 前提 最近對網路程式設計方面比較有興趣,在微服務實踐上也用到了相對主流的RPC框架如Spring Cloud Gateway底層也切換為Reactor-Netty,像Redisson底層也是使用Netty封裝通訊協議,最近調研和準備使用的S
基於Netty和SpringBoot實現一個輕量級RPC框架-Server篇
前提 前置文章: Github Page:《基於Netty和SpringBoot實現一個輕量級RPC框架-協議篇》 Coding Page:《基於Netty和SpringBoot實現一個輕量級RPC框架-協議篇》 在前置的《基於Netty和SpringBoot實現一個輕量級RPC框架-協議篇》一文中已經定
基於Netty和SpringBoot實現一個輕量級RPC框架-Client篇
前提 前置文章: 《基於Netty和SpringBoot實現一個輕量級RPC框架-協議篇》 《基於Netty和SpringBoot實現一個輕量級RPC框架-Server篇》 前一篇文章相對簡略地介紹了RPC服務端的編寫,而這篇博文最要介紹服務端(Client)的實現。RPC呼叫一般是面向契約程式設計的,而
基於Netty和SpringBoot實現一個輕量級RPC框架-Client端請求響應同步化處理
前提 前置文章: 《基於Netty和SpringBoot實現一個輕量級RPC框架-協議篇》 《基於Netty和SpringBoot實現一個輕量級RPC框架-Server篇》 《基於Netty和SpringBoot實現一個輕量級RPC框架-Client篇》 前一篇文章簡單介紹了通過動態代理完成了Client
用c#自己實現一個簡單的JSON解析器
## 一、JSON格式介紹 * JSON(JavaScript Object Notation) 是一種輕量級的資料交換格式。相對於另一種資料交換格式 XML,JSON 有著很多優點。例如易讀性更好,佔用空間更少等。在 web 應用開發領域內,得益於 JavaScript 對 JSON 提供的良好支援,JSO
自己用-Netty-實現一個簡單的-RPC
轉自:http://thinkinjava.cn/2018/03/%E8%87%AA%E5%B7%B1%E7%94%A8-Netty-%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84-RPC/ 目錄: 需求
使用akka實現一個簡單的RPC框架(一)
一、概述 目前大多數的分散式架構底層通訊都是通過RPC實現的,RPC框架非常多,比如前我們學過的Hadoop專案的RPC通訊框架,但是Hadoop在設計之初就是為了執行長達數小時的批量而設計的,在某些極端的情況下,任務提交的延遲很高,所有Hadoop的RPC顯得有些笨重。
【遠端呼叫框架】如何實現一個簡單的RPC框架(五)優化三:軟負載中心設計與實現
【如何實現一個簡單的RPC框架】系列文章: 1.前言 在部落格【遠端呼叫框架】如何實現一個簡單的RPC框架(一)想法與設計中我們介紹了“服務註冊查詢中心”,負責服務資訊的管理即服務的註冊以及查詢,在目前為止的實現中,我們採用web應用的方式,以