Handwritten RPC framework (I)
RPC(Remote Procedure Call), that is, remote procedure call, is mainly used in distributed applications. Services are deployed on different machines to call the content in the remote server through the RPC framework. Generally, the RPC framework adopts the client / Provider mode, and its main processes are:
- Client call function
- Serialize the call information (called classes, methods, method parameters, etc.), and send the data packet obtained from the serialized information to the Provider through socket and other means
- The Provider deserializes the call information from the packet. The Provider executes the request and serializes the returned results
- Send the serialized packet to the Consumer, and the Consumer deserializes the received data
This article mainly refers to articles How to implement a simple RPC , this article only implements the remote call to the Calculator class, which is lack of generality. Later, it will gradually realize the functions of service registration, load balancing and dynamic proxy, and gradually improve the RPC framework.
This paper implements an RPC framework of calculator class. The client returns the calculation results by calling the program and the server.
-
Calculator class for Provider
Interface
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); }
Implementation of calculator class
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; } } }
-
When defining a remote call, the message body sent by the client to the server needs to be serialized and delivered, so it needs to implement 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 + '\'' + '}'; } }
-
Define the logic of the Consumer when calling remotely
public class CalculatorRemoteImpl implements Calculator { //Override methods in Calculator @Override public int add(int a, int b) { //Address of remote call String address="127.0.0.1"; try{ //Establish socket connection with remote service Socket socket=new Socket(address,PORT); //Message body serialization CalculateRpcRequest calculateRpcRequest=generateRequest(a,b); //Initialize the serialization stream of the object, convert the object into byte data, and send it through socket ObjectOutputStream objectOutputStream=new ObjectOutputStream(socket.getOutputStream()); //Write the message body of the call objectOutputStream.writeObject(calculateRpcRequest); //Get return data 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(); } } //Message body instantiation 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; }
-
Define the logic of the Provider when calling remotely
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 { //Listen for data on port 8080 ServerSocket listener = new ServerSocket(8080); try { Socket socket = listener.accept(); while (true) { try { //Deserialize data ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); Object object = objectInputStream.readObject(); int res = 0; if (object instanceof CalculateRpcRequest) { CalculateRpcRequest calculateRpcRequest = (CalculateRpcRequest) object; //Judgment execution method if ("add".equals(calculateRpcRequest.getName())) { //Execute the method res = calculator.add(calculateRpcRequest.getA(), calculateRpcRequest.getB()); } else { throw new UnsupportedOperationException(); } } //Return data serialization ObjectOutputStream objectOutputStream=new ObjectOutputStream(socket.getOutputStream()); objectOutputStream.writeObject(new Integer(res)); } catch (Exception e) { System.out.println("Reverse sequence failed!"); } finally { socket.close(); } } } finally { listener.close(); } } }
-
Remote call via RPC
public class Consumer { public static void main(String[] args) { //Instantiate and initiate rpc request CalculatorRemoteImpl cal=new CalculatorRemoteImpl(); //Remote call int res=cal.add(1000,210); System.out.println("res= "+res); } }
This time, I am only familiar with the basic process and principle of RPC in the previous article, and it is difficult to expand the project through the direct proxy Calculator class in the framework, so it can not be regarded as a real RPC framework. After that, the RPC framework will be improved by implementing dynamic proxy through reflection in Java.
Project address: https://github.com/iven98/irpc.git (v1.0)