1. 程式人生 > >JAVA通訊(2)--實現簡單的RPC框架

JAVA通訊(2)--實現簡單的RPC框架

一、RPC簡介

RPC,全稱為Remote Procedure Call,即遠端過程呼叫,它是一個計算機通訊協議。它允許像呼叫本地服務一樣呼叫遠端服務。它可以有不同的實現方式。如RMI(遠端方法呼叫)、Hessian、Http invoker等。另外,RPC是與語言無關的。 RPC示意圖
這裡寫圖片描述
如上圖所示,假設Computer1在呼叫sayHi()方法,對於Computer1而言呼叫sayHi()方法就像呼叫本地方法一樣,呼叫 –>返回。但從後續呼叫可以看出Computer1呼叫的是Computer2中的sayHi()方法,RPC遮蔽了底層的實現細節,讓呼叫者無需關注網路通訊,資料傳輸等細節。

二、RPC框架的實現

上面介紹了RPC的核心原理:RPC能夠讓本地應用簡單、高效地呼叫伺服器中的過程(服務)。它主要應用在分散式系統。如Hadoop中的IPC元件。但怎樣實現一個RPC框架呢?

從下面幾個方面思考,僅供參考:

1.通訊模型:假設通訊的為A機器與B機器,A與B之間有通訊模型,在Java中一般基於BIO或NIO;。

2.過程(服務)定位:使用給定的通訊方式,與確定IP與埠及方法名稱確定具體的過程或方法;

3.遠端代理物件:本地呼叫的方法(服務)其實是遠端方法的本地代理,因此可能需要一個遠端代理物件,對於Java而言,遠端代理物件可以使用Java的動態物件實現,封裝了呼叫遠端方法呼叫;

4.序列化,將物件名稱、方法名稱、引數等物件資訊進行網路傳輸需要轉換成二進位制傳輸,這裡可能需要不同的序列化技術方案。如:protobuf,Arvo等。

三、Java實現RPC框架

1、實現技術方案
下面使用比較原始的方案實現RPC框架,採用Socket通訊、動態代理與反射與Java原生的序列化。

2、RPC框架架構
RPC架構分為三部分:

1)服務提供者,執行在伺服器端,提供服務介面定義與服務實現類。

2)服務中心,執行在伺服器端,負責將本地服務釋出成遠端服務,管理遠端服務,提供給服務消費者使用。

3)服務消費者,執行在客戶端,通過遠端代理物件呼叫遠端服務。

3、 具體實現
服務提供者介面定義與實現,程式碼如下:

public interface HelloService {

    String sayHi(String name);

}

HelloServices介面實現類:

public class HelloServiceImpl implements HelloService {

    public String sayHi(String name) {
        return "Hi, " + name;
    }

}

服務中心程式碼實現,程式碼如下:

public interface Server {
    public void stop();

    public void start() throws IOException;

    public void register(Class serviceInterface, Class impl);

    public boolean isRunning();

    public int getPort();
}

服務中心實現類:

public class ServiceCenter implements Server {
    private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    private static final HashMap<String, Class> serviceRegistry = new HashMap<String, Class>();

    private static boolean isRunning = false;

    private static int port;

    public ServiceCenter(int port) {
        this.port = port;
    }

    public void stop() {
        isRunning = false;
        executor.shutdown();
    }

    public void start() throws IOException {
        ServerSocket server = new ServerSocket();
        server.bind(new InetSocketAddress(port));
        System.out.println("start server");
        try {
            while (true) {
                // 1.監聽客戶端的TCP連線,接到TCP連線後將其封裝成task,由執行緒池執行
                executor.execute(new ServiceTask(server.accept()));
            }
        } finally {
            server.close();
        }
    }

    public void register(Class serviceInterface, Class impl) {
        serviceRegistry.put(serviceInterface.getName(), impl);
    }

    public boolean isRunning() {
        return isRunning;
    }

    public int getPort() {
        return port;
    }

    private static class ServiceTask implements Runnable {
        Socket clent = null;

        public ServiceTask(Socket client) {
            this.clent = client;
        }

        public void run() {
            ObjectInputStream input = null;
            ObjectOutputStream output = null;
            try {
                // 2.將客戶端傳送的碼流反序列化成物件,反射呼叫服務實現者,獲取執行結果
                input = new ObjectInputStream(clent.getInputStream());
                String serviceName = input.readUTF();
                String methodName = input.readUTF();
                Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
                Object[] arguments = (Object[]) input.readObject();
                Class serviceClass = serviceRegistry.get(serviceName);
                if (serviceClass == null) {
                    throw new ClassNotFoundException(serviceName + " not found");
                }
                Method method = serviceClass.getMethod(methodName, parameterTypes);
                Object result = method.invoke(serviceClass.newInstance(), arguments);

                // 3.將執行結果反序列化,通過socket傳送給客戶端
                output = new ObjectOutputStream(clent.getOutputStream());
                output.writeObject(result);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (output != null) {
                    try {
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (input != null) {
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (clent != null) {
                    try {
                        clent.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }
}

 客戶端的遠端代理物件:

public class RPCClient<T> {
    public static <T> T getRemoteProxyObj(final Class<?> serviceInterface, final InetSocketAddress addr) {
        // 1.將本地的介面呼叫轉換成JDK的動態代理,在動態代理中實現介面的遠端呼叫
        return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface},
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Socket socket = null;
                        ObjectOutputStream output = null;
                        ObjectInputStream input = null;
                        try {
                            // 2.建立Socket客戶端,根據指定地址連線遠端服務提供者
                            socket = new Socket();
                            socket.connect(addr);

                            // 3.將遠端服務呼叫所需的介面類、方法名、引數列表等編碼後傳送給服務提供者
                            output = new ObjectOutputStream(socket.getOutputStream());
                            output.writeUTF(serviceInterface.getName());
                            output.writeUTF(method.getName());
                            output.writeObject(method.getParameterTypes());
                            output.writeObject(args);

                            // 4.同步阻塞等待伺服器返回應答,獲取應答後返回
                            input = new ObjectInputStream(socket.getInputStream());
                            return input.readObject();
                        } finally {
                            if (socket != null) socket.close();
                            if (output != null) output.close();
                            if (input != null) input.close();
                        }
                    }
                });
    }
}

最後為測試類:

public class RPCTest {

    public static void main(String[] args) throws IOException {
        new Thread(new Runnable() {
            public void run() {
                try {
                    Server serviceServer = new ServiceCenter(8088);
                    serviceServer.register(HelloService.class, HelloServiceImpl.class);
                    serviceServer.start();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        HelloService service = RPCClient.getRemoteProxyObj(HelloService.class, new InetSocketAddress("localhost", 8088));
        System.out.println(service.sayHi("test"));
    }
}

執行結果:

regeist service HelloService
start server
Hi, test

四、總結

  RPC本質為訊息處理模型,RPC遮蔽了底層不同主機間的通訊細節,讓程序呼叫遠端的服務就像是本地的服務一樣。

五、可以改進的地方

 這裡實現的簡單RPC框架是使用Java語言開發,與Java語言高度耦合,並且通訊方式採用的Socket是基於BIO實現的,IO效率不高,還有Java原生的序列化機制佔記憶體太多,執行效率也不高。可以考慮從下面幾種方法改進。

可以採用基於JSON資料傳輸的RPC框架;
可以使用NIO或直接使用Netty替代BIO實現;
使用開源的序列化機制,如Hadoop Avro與Google protobuf等;
服務註冊可以使用Zookeeper進行管理,能夠讓應用更加穩定。

—————————————————————————————————————————————————–

java架構師專案實戰,高併發叢集分散式,大資料高可用視訊教程,共760G

下載地址:

https://item.taobao.com/item.htm?id=555888526201

01.高階架構師四十二個階段高
02.Java高階系統培訓架構課程148課時
03.Java高階網際網路架構師課程
04.Java網際網路架構Netty、Nio、Mina等-視訊教程
05.Java高階架構設計2016整理-視訊教程
06.架構師基礎、高階片
07.Java架構師必修linux運維繫列課程
08.Java高階系統培訓架構課程116課時
+
hadoop系列教程,java設計模式與資料結構, Spring Cloud微服務, SpringBoot入門
—————————————————————————————————————————————————–

相關推薦

JAVA通訊(2)--實現簡單RPC框架

一、RPC簡介 RPC,全稱為Remote Procedure Call,即遠端過程呼叫,它是一個計算機通訊協議。它允許像呼叫本地服務一樣呼叫遠端服務。它可以有不同的實現方式。如RMI(遠端方法呼叫)、Hessian、Http invoker等。另外,RPC是

Scala學習筆記(10)—— Akka 實現簡單 RPC 框架

1 Akka 介紹 目前大多數的分散式架構底層通訊都是通過RPC實現的,RPC框架非常多,比如前我們學過的Hadoop專案的RPC通訊框架,但是Hadoop在設計之初就是為了執行長達數小時的批量而設計的,在某些極端的情況下,任務提交的延遲很高,所有Hadoop的

一個簡單RPC框架是如何煉成的(IV)——實現RPC訊息的編解碼

之前我們制定了一個很簡單的RPC訊息 的格式,但是還遺留了兩個問題,上一篇解決掉了一個,還留下一個 我們並沒有實現相應的encode和decode方法,沒有基於可以跨裝置的字串傳輸,而是直接的記憶體變數傳遞。 現在的RPC request不支援帶引數的請求命令。如add(a,

一個簡單RPC框架是如何煉成的(III)——實現帶引數的RPC呼叫

上一篇,我們制定了一個很簡單的RPC訊息 的格式,但是還遺留了兩個問題 我們並沒有實現相應的encode和decode方法,沒有基於可以跨裝置的字串傳輸,而是直接的記憶體變數傳遞。 現在的RPC request不支援帶引數的請求命令。如add(a, b), 如何在RPC訊息中描述

使用Java實現簡易RPC框架

RPC其全程為Remote Process Call,即為遠端過程呼叫。RPC將傳統的本地呼叫轉換為呼叫遠端的伺服器的方法,給系統的處理能力和吞吐量帶來了極大的提升。 隨著專案的發展,業務越來越複雜,單個專案的話,會非常複雜,且不易維護,如果單個專案掛了

java 網路通訊socket實現簡單例項

Socket通訊的步驟                  ① 建立ServerSocket和Socket                  ② 開啟連線到Socket的輸入/輸出流          

BootNettyRpc:采用 Netty 實現RPC 框架

ofo 文件配置 RR 實現 端口 監控 ble tin cto 什麽是 BootNettyRpc?BootNettyRpc 是一個采用Netty實現的Rpc框架,適用於Spring Boot項目,支持Spring Cloud。 目前支持的版本為Spring Boot 1.

一個簡單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 這裡,直接使

Java編寫基於netty的RPC框架

-c shu resp pre 覆蓋 數據處理 ofo 核心 用兩個 一 簡單概念 RPC: ( Remote Procedure Call),遠程調用過程,是通過網絡調用遠程計算機的進程中某個方法,從而獲取到想要的數據,過程如同調用本地的方法一樣. 阻塞IO :當阻塞

手寫簡單 rpc 框架

手寫簡單 rpc 框架 手寫簡單 rpc 框架 0 緣起 1 技術選型 2 框架思路 3 呼叫方式 3.1 定義服務 3.2 定義 bean

java集合類實現簡單的學生資訊管理系統

package jihe; import java.util.Scanner; public class Student { private String sno; private String sname; private int grade; private int age; private S

Java語言快速實現簡單MQ訊息佇列服務

目錄 MQ基礎回顧 主要角色 自定義協議 流程順序 專案構建流程 具體使用流程 程式碼演示 訊息處理中心 Broker 訊息處理中心服務 BrokerServer 客戶端 MqClient 測試MQ 小結

java 連結redis 實現簡單的查詢

1  新建maven專案 新增依賴  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schema

java基於jsoup實現簡單的圖片爬蟲並下載

2018年11月04日 17:20:32 小小申 閱讀數:4 標籤: jsoup java

netty實現RPC框架

自己手擼了一個nettyRPC框架,希望在這裡給有興趣的同學們做個參考。 要想實現nettyrpc需要了解的技術要點如下: spring的自定義註解、spring的bean的有關初始化。 反射和動態代理的使用。 瞭解socket的使用。 瞭解zookeeper的使用。 瞭解nio原理。 瞭解netty的基本操

***Java中WebSocket實現簡單的聊天***

Java中WebSocket實現簡單的聊天 1 在pom.xml中新增Jar包依賴 <dependency> <groupId>org.springframework.boot</groupId> <arti

[Java] 原創ssm實現簡單的客戶管理系統

執行環境jdk8+tomcat8+mysql+eclipse專案技術spring+spring mvc+mybatis+bootstrap+jqueryjar包檔案jar包一併在專案裡面,匯入專案直接執行專案截圖執行截圖 http://localhost :8080/SSMB

JAVA用於List實現簡單的學生管理系統

作為一名JAVA的程式設計師,無論初學者也好大神也好,學生管理系統是個很好例子,初學者用陣列、list等來寫簡單的學生管理系統,大神則是用swing+資料庫做有介面的學生管理系統,廢話不多說,今天我就用List來實現學生管理系統。 學生管理系統主要針對學生,我