How to design an RPC framework full of compromises

What is the RPC

Remote Procedure Call (RPC) refers to Remote method Call, mainly to solve the problem of method Call between different services in microservice system. Generally speaking, the caller only needs to use the interface provided by the service provider, and the rest needs to be done by the RPC framework. In brief, the RPC framework needs to do the following work.

  • Network communication, responsible for communication between service providers and callers
  • Proxy interface, which requires the method interface provided by the proxy service provider so that the service caller can easily invoke the method
  • Service invocation, which invokes the service provider’s methods on request and returns data to the service caller
  • Service registration and discovery, equivalent to the address book function to know which people (services) can provide which functions

Network Communication design

We can use HTTP or custom communication protocol for network communication. Personally, I hate HTTP. Therefore, the communication protocol, using TCP protocol, and define the format of the packet as follows.

  • The first four bytes of the packet are used to mark the length of the packet body.
  • The packet body holds the data we want to transmit

Data serialization format

There are many options for the data sequence approach, but we chose to compromise to use JSON as our serialization format for convenience (again full of compromises).

Network communication framework

Network Communication Framework We use NetTY as our communication framework and use a synchronous non-blocking communication model, for which we need to solve the problems caused by this model see the service Invocation section. (Small volume of Netty tutorial available)

The agent interface

Typically, the service provider will publish a JAR package with only interfaces and POJO classes for the service caller to use, so our RPC framework will need to proxy these interfaces and register the proxied interfaces as beans in the Spring container if it needs to integrate with Spring.

The Client package should contain only the interfaces to be exposed by the service provider and the POJO class Service contains the implementation classes for the interfaces in the client package

The service call

Because we use NetTY (synchronous non-blocking), we need to design a scheme for this, the network communication model adopts synchronous non-blocking scheme, but in the client side call method return result is blocked.

Blocking design scheme

The blocking design, which typically waits until the service provider returns data after the service caller sends the service provider an invocation instruction, is simple, clear, and compromised.

Synchronous non-blocking design solutions

Because of the synchronous non-blocking approach to communication, it is impossible to keep blocking waiting for the service provider to return the data. In order to simulate this blocking effect, the following design is used.

After the client sends a request to the server, it immediately returns to FutureTask and generates a transaction ID to bind the transaction to Callable. Using the features of FutureTask, we can achieve blocking effect.

To return data, you only need to fetch the corresponding Callable setting value according to the returned transaction ID and call the Call method.

Service registration and discovery

The sign-up for the service found can be understood as a phone address book feature, meaning you know which friends are for what, which are tool people, which are backup…

Therefore, as long as the data can be stored, it can be used for service discovery and registration in theory, no matter redis,mysql, ZooKeeper or their own implementation.

conclusion

Basically, the RPC frame has two words written all over it, call it TuoXieRpc.