从零手写RPC

RPC概述

RPC(Remote Proceduce Call 远程过程调用) 一般用来实现部署在不同机器上的系统之间的方法调用,使程序能够像访问本地系统资源一样,通过网络传输过去访问远端系统资源。

RPC 调用过程

  1. Client 客户端调用方法实现,负责发起RPC调用。
  2. ClientStub/SereverStub 可以看作一个代理对象,屏蔽RPC调用过程中复杂的网络处理逻辑,使RPC透明化,使得调用远程方法想调用本地方法一样。
  3. Server 服务端提供远程服务。

Stub 主要作用

  • 序列化:负责数据的序列化发序列化。
  • 网络传输:数据发送与接收。

注:ServerStub又叫Skeleton。

RPC 实现

1. 远程服务接口

public interface IHello {
    public String sayHello(String info);
}

2. 远程服务接口实现类(Server)

public class HelloService implements IHello {
    public String sayHello(String info) {
        String result = "hello : " + info;
        System.out.println(result);
        return result;
    }
}

提供服务实现的类。

3.服务器代理实现(Skeleton)

public class RpcProxyServer {
    private IHello hello = new HelloService();

    public void publisherServer(int port) {
        try (ServerSocket ss = new ServerSocket(port)) {
            while (true) {
                try (Socket socket = ss.accept()) {
                    try (ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {
                        String method = ois.readUTF();
                        Object[] objs = (Object[]) ois.readObject();
                        Class<?>[] types = new Class[objs.length];
                        for (int i = 0; i < types.length; i++) {
                            types[i] = objs[i].getClass();
                        }
                        Method m = HelloService.class.getMethod(method, types);
                        Object obj = m.invoke(hello, objs);

                        try (ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream())) {
                            oos.writeObject(obj);
                            oos.flush();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  1. 构建一个ServerSocket服务监听来自客户端的请求。
  2. 接收请求的数据。(方法名和参数)
  3. 根据请求的数据(方法名和参数),使用反射调用相应的服务。
  4. 输出服务的响应数据。

4. RPC 客户端代理实现(ClientStub)

public class RpcProxyClient<T> {

    public T proxyClient(Class<T> clazz) {
        return (T) clazz.cast(Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        try (Socket socket = new Socket("localhost", 8000)) {
                            try (ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream())) {
                                oos.writeUTF(method.getName());
                                oos.writeObject(args);
                                oos.flush();

                                try (ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {
                                    return ois.readObject();
                                }
                            }
                        }
                    }
                }));
    }
}
  1. 构建一个Socket,连接远程服务。
  2. 向远程服务发送数据。(方法名和方法参数)
  3. 接收远程服务响应的数据。

5.服务端发布服务

public class RpcServer {
    //发布服务
    public static void main(String[] args) {
        RpcProxyServer server = new RpcProxyServer();
        server.publisherServer(8000);
    }
}

6.客户端调用(Client)

public class RpcClient {
    // 调用服务
    public static void main(String[] args) {
        RpcProxyClient<HelloService> rpcClient = new RpcProxyClient<>();

        IHello hello = rpcClient.proxyClient(HelloService.class);
        String s = hello.sayHello("dd");
        System.out.println(s);
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏安恒网络空间安全讲武堂

Python编写渗透工具学习笔记二 | 0x03用python构建ssh僵尸网络

0x03用python构建ssh僵尸网络 1用pxssh暴力破解ssh密码 因为默认情况下只有linux有ssh服务,所以此脚本只适用于在linux下使用 靶机...

5917
来自专栏闻道于事

多线程基础必要知识点!看了学习多线程事半功倍

3263
来自专栏斑斓

PySpark分析二进制文件

客户需求 客户希望通过spark来分析二进制文件中0和1的数量以及占比。如果要分析的是目录,则针对目录下的每个文件单独进行分析。分析后的结果保存与被分析文件同名...

3974
来自专栏个人分享

Spark shuffle详细过程

有许多场景下,我们需要进行跨服务器的数据整合,比如两个表之间,通过Id进行join操作,你必须确保所有具有相同id的数据整合到相同的块文件中。那么我们先说一下m...

3442
来自专栏奔跑的蛙牛技术博客

java 读写二进制数据与java序列化

zip文档以压缩格式存储一个和多个文件,每个ZIP文件都有一个头,包含每个文件的名字和压缩方法等信息

1582
来自专栏java 成神之路

基于ReentrantLock发生死锁的解决方案

3256
来自专栏牛肉圆粉不加葱

【实战篇】如何优雅的停止你的 Spark Streaming Application

你的一个 spark streaming application 已经好好运行了一段时间了,这个时候你因为某种原因要停止它。你应该怎么做?直接暴力 kill 该...

1704
来自专栏点滴积累

geotrellis使用(三十三)关于Geotrellis读取Geotiff的两个细节

前言 在上两篇文章中我介绍了如何直接将Geotiff(一个或者多个)发布为TMS服务。这中间其实我遇到了一个问题,并且这个问题伴随Geotrellis的几乎所有...

41211
来自专栏向治洪

android分包方案

当一个app的功能越来越复杂,代码量越来越多,也许有一天便会突然遇到下列现象: 1. 生成的apk在2.3以前的机器无法安装,提示INSTALL_FAILE...

20510
来自专栏大内老A

认识ASP.NET MVC的5种AuthorizationFilter

在总体介绍了筛选器及其提供机制(《深入探讨ASP.NET MVC的筛选器》)之后,我们按照执行的先后顺序对四种不同的筛选器进行单独介绍,首先来介绍最先执行的Au...

2416

扫码关注云+社区

领取腾讯云代金券