RPC

Problem solved

RPC is primarily designed to solve two problems:

  • To solve the invocation problem between services in distributed system.

  • The remote call should be as convenient as the local call, so that the caller is not aware of the logic of the remote call.

In this section we will learn how to implement the simplest RPC calls based on WebSocket, and we will implement a version based on Netty4 later.

Open source address: github.com/houbb/rpc

Complete the process

The Client on the left corresponds to Service A, and the Server on the right corresponds to Service B.

Let’s explain it step by step.

  1. In the application-layer code of Service A, the Add method of an implementation class of Calculator is called, hoping to perform an addition operation.

  2. The Calculator implementation class does not directly realize the Calculator’s addition, subtraction, multiplication and division logic, but obtains the calculation result by remotely calling the RPC interface of Service B, so it is called Stub.

  3. How does Stub establish remote communication with Service B? The run-time Library will help you implement remote communication functions, such as the Java Socket Library. Of course, you can also use HttpClient, which is based on the Http protocol, or any other communication tool class. RPC does not specify which protocol you want to use to communicate;

  4. Stub establishes communication with Service B by invoking methods provided by the communication tool, and then sends the request data to Service B. Note that since the underlying network communication is in binary format, the data that the Stub passes to the communication class must also be binary, such as Calculator.add (1,2). You must put the parameters 1 and 2 into a Request object. This includes other information such as which RPC interface of which service to call), which is then serialized to binary and passed to the communication utility class, as shown in the code implementation below;

  5. The binary data goes to Service B, which of course has its own communication tool that receives binary requests.

  6. Since the data is binary, deserialize the binary data into a request object, which is then handed to the Service B Stub for processing.

  7. Just like the Service A Stub, it is A “Stub” that parses the request object, knows which RPC interface the caller is calling, and then passes the parameters to the corresponding RPC interface. The actual implementation class of Calculator to execute. Obviously, if it’s Java, reflection is used here.

  8. After the RPC interface is executed, the result is returned. Now Service B needs to send data to Service A. Service B becomes A Client and Service A becomes A Server. Service B deserialization execution result -> transfer to Service A->Service A deserialization execution result -> return the result to Application.

Simple implementation

Suppose service A wants to call A method of service B.

It cannot be used directly because it is not in the same memory. How can you implement something like Dubbo?

Instead of using HTTP level communication, use TCP.

common

A common module that defines common objects.

  • Rpc constants
public interface RpcConstant {

    /** * address */
    String ADDRESS = "127.0.0.1";

    /** * port number */
    int PORT = 12345;

}
Copy the code
  • The request into the reference
public class RpcCalculateRequest implements Serializable {

    private static final long serialVersionUID = 6420751004355300996L;

    /** ** /
    private int one;

    /** ** /
    private int two;

    //getter & setter & toString()
}
Copy the code
  • The service interface
public interface Calculator {

    /** * computes addition *@paramParameter one: one *@paramTwo Parameter two *@returnReturns the result */
    int add(int one, int two);

}
Copy the code

server

  • Implementation of the service interface
public class CalculatorImpl implements Calculator {

    @Override
    public int add(int one, int two) {
        returnone + two; }}Copy the code
  • Start the service
public static void main(String[] args) throws IOException {
    Calculator calculator = new CalculatorImpl();
    try (ServerSocket listener = new ServerSocket(RpcConstant.PORT)) {
        System.out.println("Server startup:" + RpcConstant.ADDRESS + ":" + RpcConstant.PORT);
        while (true) {
            try (Socket socket = listener.accept()) {
                // Deserialize the request
                ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                Object object = objectInputStream.readObject();
                System.out.println("Request is: " + object);
                // Invoke the service
                int result = 0;
                if (object instanceof RpcCalculateRequest) {
                    RpcCalculateRequest calculateRpcRequest = (RpcCalculateRequest) object;
                    result = calculator.add(calculateRpcRequest.getOne(), calculateRpcRequest.getTwo());
                }
                // Return the result
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                objectOutputStream.writeObject(result);
            } catch(Exception e) { e.printStackTrace(); }}}}Copy the code

Startup log:

Server startup: 127.0.0.1:12345Copy the code

client

  • Client call
public static void main(String[] args) {
    Calculator calculator = new CalculatorProxy();
    int result = calculator.add(1.2);
    System.out.println(result);
}
Copy the code
  • The computed proxy class
public class CalculatorProxy implements Calculator {

    @Override
    public int add(int one, int two) {
        try {
            Socket socket = new Socket(RpcConstant.ADDRESS, RpcConstant.PORT);

            // Serialize the request
            RpcCalculateRequest calculateRpcRequest = new RpcCalculateRequest(one, two);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());

            // Send the request to the service provider
            objectOutputStream.writeObject(calculateRpcRequest);

            // Deserialize the response body
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            Object response = objectInputStream.readObject();

            if (response instanceof Integer) {
                return (Integer) response;
            } else {
                throw newRuntimeException(); }}catch (IOException | ClassNotFoundException e) {
            throw newRuntimeException(e); }}}Copy the code
  • Call log

The client side

3
Copy the code

The server side

Server startup: 127.0.0.1:12345 Request is: RpcCalculateRequest{one=1, two=2}Copy the code

Open source address

In order to facilitate learning, the above source code has been open source:

github.com/houbb/rpc

I am old ma, looking forward to meeting with you next time.