RPC mainly aims to solve two problems:
In this section, let's learn how to implement the simplest rpc call based on websocket, and then we will implement a version based on netty4
.
The Client on the left corresponds to Service A in front, and the Server on the right corresponds to Service B.
Let's explain in detail step by step below.
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)This Calculator
implementation class does not directly implement the calculator's addition, subtraction, multiplication, and division logic internally, but instead obtains the result of the operation by remotely calling the RPC interface of Service B, so it is called Stub;
3)How does Stub establish remote communication with Service B? At this time, you will use the remote communication tool, which is the Run-time Library in the figure. This tool will help you realize the function of remote communication. For example, Java's Socket is such a library. Of course, you can also use Http-based Protocol HttpClient
, or other communication tools, can be used, RPC does not specify which protocol you want to use for communication;
4)Stub establishes communication with Service B by calling the method provided by the communication tool, and then sends the request data to Service B. It should be noted that since the underlying network communication is based on a binary format, the data sent by the stub to the communication tool must also be binary, such as calculator.add(1,2)
, you must put the parameter values 1 and 2 To a Request object (of course, this Request object not only contains this information, but also includes other information such as which RPC interface of which service is to be called), and then serialize it into binary, and then pass it to the communication tool class. This will also be in the following code Reflected in realization;
5)The binary data is transmitted to Service B. Of course, Service B also has its own communication tool, which receives binary requests through this communication tool;
6)Since the data is binary, it is natural to deserialize the binary data into the request object, and then hand the request object to the Stub of Service B for processing;
7)Like the previous Service A Stub, the Stub here is also a "fake". It is only responsible for parsing the request object, knowing which RPC interface the caller wants to call, and what are the incoming parameters. Then pass these parameters to the corresponding RPC interface, which is the actual implementation class of Calculator for execution. Obviously, if it is Java, then reflection must be used here.
8)After the RPC interface is executed, the execution result is returned. Now it is Service B's turn to send the data to Service A. How to send it? The same reason, the same process, but now Service B becomes Client and Service A becomes Server: Service B deserialization execution result -> transfer to Service A -> Service A deserialization execution result -> will The result is returned to Application, complete.
Suppose service A wants to call a method of service B.
Because they are not in the same memory, they cannot be used directly. How can a function similar to Dubbo be achieved?
There is no need to use HTTP level communication, just use TCP protocol.
Common modules define common objects.
Rpc constant
public interface RpcConstant {
/**
* address
*/
String ADDRESS = "127.0.0.1";
/**
* The port number
*/
int PORT = 12345;
}
Request for participation
public class RpcCalculateRequest implements Serializable {
private static final long serialVersionUID = 6420751004355300996L;
/**
* Parameter one
*/
private int one;
/**
* Parameter two
*/
private int two;
//getter & setter & toString()
}
Service interface
public interface Calculator {
/**
* Calculate addition
* @param one parameter one
* @param two parameter two
* @return return result
*/
int add(int one, int two);
}
Implementation of the service interface
public class CalculatorImpl implements Calculator {
@Override
public int add(int one, int two) {
return one + two;
}
}
start service
public static void main(String[] args) throws IOException {
Calculator calculator = new CalculatorImpl();
try (ServerSocket listener = new ServerSocket(RpcConstant.PORT)) {
System.out.println("Server start:" + 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);
// call service
int result = 0;
if (object instanceof RpcCalculateRequest) {
RpcCalculateRequest calculateRpcRequest = (RpcCalculateRequest) object;
result = calculator.add(calculateRpcRequest.getOne(), calculateRpcRequest.getTwo());
}
// return result
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Start log:
Server start: 127.0.0.1:12345
Client call
public static void main(String[] args) {
Calculator calculator = new CalculatorProxy();
int result = calculator.add(1, 2);
System.out.println(result);
}
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 new RuntimeException();
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
Call log
client side
3
server side
Server start: 127.0.0.1:12345
Request is: RpcCalculateRequest{one=1, two=2}