1. 程式人生 > >自己實現一個簡單的RPC框架

自己實現一個簡單的RPC框架

RPC的全稱是Remote Procedure Call,它是一種程序間的通訊方式。允許像呼叫本地服務一樣呼叫遠端服務。

對於RPC的總結:

  1. 簡單:RPC概念的語義十分簡單和清晰,這樣建立分散式計算更容易。
  2. 高效:過程呼叫看起來十分簡單而且十分高效。
  3. 通用:在單機計算過程中往往是不同演算法和API,在跨程序呼叫最重要的是通用的通訊機制。

對於RPC框架實現的技術點總結如下:

  1. 遠端服務提供者需要一某種形式提供服務呼叫的相關資訊,包括但不限於服務介面的定義、資料結構,或者中間狀態服務定義的檔案。
  2. 遠端代理物件:服務呼叫者呼叫的服務實際上市遠端服務的代理。
  3. 通訊:RPC框架的與具體的通訊協議無關
  4. 序列化:遠端通訊需要將物件轉換成二進位制碼流進行傳輸,不同的序列化框架支援的資料型別,資料包大小,異常型別及效能等都不相同。

最簡單的RPC框架實現:

  1. 服務提供者,執行在服務端,負責服務介面定義和服務介面實現。
  2. 服務釋出者,執行在RPC服務端,負責將本地服務釋出成遠端服務,供其他消費者呼叫
  3. 本地服務代理,執行在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();
                }
            }
        }
    }

}

服務釋出者的主要職責

  1. 作為服務端,監聽客戶端的TCP連結,接收到客戶端的餓連結之後封裝成Tsak交給執行緒池執行
  2. 將客戶端傳送的碼流反序列化成物件,反射呼叫服務的實現者,獲取執行結果
  3. 將結果反序列化,通過Socket傳送給客戶端
  4. 遠端服務呼叫結束後,釋放資源

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;
        });
    }

}

本地服務的代理的功能如下

  1. 將本地介面呼叫轉換成JDK的動態代理,在動態代理中實現介面的呼叫
  2. 建立Socket客戶端,根據指定地址呼叫遠端服務的提供者
  3. 將遠端介面呼叫所需要的介面類,方法名,引數列表等編碼後傳送給服務者
  4. 同步阻塞等待服務端返回應答,獲取執行結果

測試程式碼

/**
 * @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應用的方式,以