【手写RPC框架(一)】简单RPC实现

一、RPC基本结构

在这里插入图片描述
\quad\quad RPC(Remote Procedure Call),即远程过程调用,主要应用在分布式应用中,将服务部署在不同的机器上,通过RPC框架调用远程服务器中的内容。RPC 框架包含三个最重要的组件,分别是客户端服务端注册中心。在一次 RPC 调用流程中,这三个组件是这样交互的:

  • 服务端在启动后,会将它提供的服务列表发布到注册中心,客户端向注册中心订阅服务地址;
  • 客户端会通过本地代理模块 Proxy 调用服务端,Proxy 模块收到负责将方法、参数等数据转化成网络字节流;
  • 客户端从服务列表中选取其中一个的服务地址,并将数据通过网络发送给服务端;
  • 服务端接收到数据后进行解码,得到请求信息;
  • 服务端根据解码后的请求信息调用对应的服务,然后将调用结果返回给客户端。

二、实现简单的RPC

\quad\quad 但本文主要参考手写RPC框架(一)如何实现一个简单的RPC来实现简单的RPC框架,简单RPC框架采用客户端(Consumer)/服务端(Provider)的模式,这里并没有实现注册中心。同时这篇文章仅仅实现了对Calculator这个类的远程调用,缺乏通用性,之后会逐步实现服务注册动态代理负载均衡等功能,逐步完善该RPC框架。
\quad\quad 本文实现的是一个计算器类的RPC框架,客户端通过调取程序,服务端来返回计算结果,其主要流程如下

  • 客户端调用函数,如add()
  • 将调用信息(调用的类、方法、方法传参等)序列化,并通过socket等方式将序列化信息得到的数据包发送给Provider
  • Provider从数据包中反序列化得到调用信息,Provider执行请求,并将返回结果序列化
  • 将序列化后的数据包发给Consumer,Consumer反序列化接收到的数据

2.1 服务端

2.1.1 接口、实现类和序列化
  1. 计算器类接口
public interface Calculator {
    int add(int a, int b);

    //int sub(int a, int b);

    //int mul(int a, int b);

    //int div(int a, int b);
}
  1. 计算器类接口实现
public class CalculatorImpl implements Calculator{
    @Override
    public int add(int a, int b) {
        return a+b;
    }
/*
    @Override
    public int sub(int a, int b) {
        return a-b;
    }

    @Override
    public int mul(int a, int b) {
        return a*b;
    }

    @Override
    public int div(int a, int b) {
        if(b==0){
            throw new ArithmeticException();
        }
        else{
            return a/b;
        }
    }
 */
}
  1. 定义远程调用时,客户端发送给服务端的消息体,因为该类需要序列化后传递,所以需要实现Serializable
package org.example;

import java.io.Serializable;

public class CalculateRpcRequest implements Serializable {
    private static final long serialVersionUID = 7503710091945320739L;
    private int a;
    private int b;
    private String name;

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "CalculateRpcRequest{" +
                "a=" + a +
                ", b=" + b +
                ", name='" + name + '\'' +
                '}';
    }
}
2.1.2 服务端逻辑

\quad\quad 接收客户端序列化后的请求信息,执行对应方法。如本文中此处接收add方法及a,b变量,故在服务端调用add()方法实现类进行计算,并将计算结果序列化后传递给客户端。

public class Server {
    private final Calculator calculator = new CalculatorImpl();

    public static void main(String[] args) throws IOException {
        new Server().run();
    }

    public void run() throws IOException {
        //8889端口上监听数据
        ServerSocket listener = new ServerSocket(8889);
        try {
            Socket socket = listener.accept();
            while (true) {
                try {
                    //反序列化数据
                    ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                    Object object = objectInputStream.readObject();
                    int res = 0;
                    if (object instanceof CalculateRpcRequest) {
                        CalculateRpcRequest calculateRpcRequest = (CalculateRpcRequest) object;
                        //判断执行方法
                        if ("add".equals(calculateRpcRequest.getName())) {
                            //执行该方法
                            res = calculator.add(calculateRpcRequest.getA(), calculateRpcRequest.getB());
                        } else {
                            throw new UnsupportedOperationException();
                        }
                    }
                    //返回数据序列化
                    ObjectOutputStream objectOutputStream=new ObjectOutputStream(socket.getOutputStream());
                    objectOutputStream.writeObject(new Integer (res));
                } catch (Exception e) {
                    System.out.println("反序列失败!");
                } finally {
                    socket.close();
                }
            }
        } finally {
            listener.close();
        }
    }
}

2.2 客户端

2.2.1 客户端逻辑

\quad\quad 客户端发送请求信息,将调用方法和参数序列化后传递给服务端,并接收方法执行后返回的结果。

public class CalculatorRemoteImpl implements Calculator {
    //重写Calculator中的方法
    @Override
    public int add(int a, int b) {
        //远程调用的地址
        String address="127.0.0.1";
        try{
            //和远程服务建立socket连接
            Socket socket=new Socket(address,PORT);
            //消息体序列化
            CalculateRpcRequest calculateRpcRequest=generateRequest(a,b);
            //初始化对象的序列化流,把对象转成字节数据,并通过socket发送
            ObjectOutputStream objectOutputStream=new ObjectOutputStream(socket.getOutputStream());
            //写入调用的消息体
            objectOutputStream.writeObject(calculateRpcRequest);
            //获取返回数据
            ObjectInputStream objectInputStream=new ObjectInputStream(socket.getInputStream());
            Object response =objectInputStream.readObject();
            System.out.println(response);
            if(response instanceof Integer){
                return (Integer) response;
            }
            else{
                throw new InternalError();
            }
        } catch (Exception e) {
            System.out.println("ERROR!");
            throw new InternalError();
        }
    }

    //消息体实例化
    public CalculateRpcRequest generateRequest(int a,int b){
        CalculateRpcRequest calculateRpcRequest=new CalculateRpcRequest();
        calculateRpcRequest.setA(a);
        calculateRpcRequest.setB(b);
        calculateRpcRequest.setName("add");
        return calculateRpcRequest;
    }
    public static final int PORT = 8889;
}

2.3 通过RPC远程调用

public class Client {

    public static void main(String[] args) {
        //实例化,发起rpc请求
        CalculatorRemoteImpl cal=new CalculatorRemoteImpl();
        //远程调用
        int res=cal.add(1000,210);
        System.out.println("res= "+res);
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值