手写RPC框架(一)
手写RPC框架(一)
RPC(Remote Procedure Call),即远程过程调用,主要应用在分布式应用中,将服务部署在不同的机器上,通过RPC框架调用远程服务器中的内容。通常RPC框架采用客户端(Consumer)/服务端(Provider)的模式,其主要流程是:
- 客户端调用函数
- 将调用信息(调用的类、方法、方法传参等)序列化,并通过socket等方式将序列化信息得到的数据包发送给Provider
- Provider从数据包中反序列化得到调用信息,Provider执行请求,并将返回结果序列化
- 将序列化后的数据包发给Consumer,Consumer反序列化接收到的数据
本文主要参考文章如何实现一个简单的RPC,这篇文章仅仅实现了对Calculator这个类的远程调用,缺乏通用性,之后会逐步实现 服务注册,负载均衡,动态代理等功能,逐步完善该RPC框架。
本文实现的是一个计算器类的RPC框架,客户端通过调取程序,服务端来返回计算结果。
-
Provider的计算器类
接口
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); }
计算器类的实现
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; } } }
-
定义远程调用时,客户端发送给服务端的消息体,因为该类需要序列化后传递,所以需要实现Serializable
package request; 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 + '\'' + '}'; } }
-
定义远程调用时Consumer的逻辑
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 = 8080; }
-
定义远程调用时Provider的逻辑
public class Provider { private final Calculator calculator = new CalculatorImpl(); public static void main(String[] args) throws IOException { new Provider().run(); } public void run() throws IOException { //8080端口上监听数据 ServerSocket listener = new ServerSocket(8080); 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(); } } }
-
通过RPC远程调用
public class Consumer { public static void main(String[] args) { //实例化,发起rpc请求 CalculatorRemoteImpl cal=new CalculatorRemoteImpl(); //远程调用 int res=cal.add(1000,210); System.out.println("res= "+res); } }
本次只熟悉了上篇文章中的RPC基本的流程和原理,且该框架中通过直接代理Calculator类的方式,难以对项目进行扩展,不能算一个真正意义的RPC框架。之后会通过Java中通过反射实现动态代理的方式将RPC框架完善。
项目地址:https://github.com/iven98/irpc.git (v1.0)