01 Core Principle Can You Draw a Diagram to Explain the Communication Process of Rpc

01 Core Principle - Can you draw a diagram to explain the communication process of RPC #

Hello, I’m He Xiaofeng. If you have been working in development for a few years, I believe you are familiar with the term RPC. Before writing this column, I specifically checked the Baidu Index for RPC and found that the search trend for RPC has been steadily increasing over the years. This indirectly indicates that this technology is gradually infiltrating into our daily development. As the first lecture of this column, I would like to focus on the word “RPC” and discuss its definition, the problems it aims to solve, and its working principles.

In the past few years, when I interviewed engineers, I liked to ask them a question, “Can you explain the communication process of RPC to me?” This question is actually not difficult, but because many engineers are usually using various frameworks, they may not have stopped to think about the principles of the frameworks. So, after asking this question, some people hesitate and fail to explain it clearly.

Then, I would guide them and say, “Think about it, if there is no RPC framework, how would you invoke an interface on another server?” You see, this question can be deep or shallow, and it also tests the candidate’s fundamentals. If you were the candidate, how would you answer? Today, I will try to answer this question for you.

What is RPC? #

I know you probably don’t like listening to concepts, and I’m the same way. When I read books, I skip over definitions directly. However, later on, I discovered how great it is to have a “definition.” When we can define something in one sentence, it indirectly shows that we have a thorough understanding of it. Not only do we know what problem it solves, but we also know its boundaries. So, you can take a moment to think about what RPC is.

The full name of RPC is Remote Procedure Call, which means remote procedure call. By interpreting its literal meaning, “remote” certainly refers to cross-machine rather than local, so it requires network programming to be implemented. But does that mean any application that accesses another machine through network communication can be called an RPC call? Obviously, that’s not enough.

I understand RPC as helping us hide the details of network programming and achieving the same experience as calling local (within the same project) methods. We don’t need to write a lot of code unrelated to business just because this method is a remote call.

It’s like a bridge built over a small river, connecting the two banks of the river. Without the bridge, we would need to use other methods such as rowing a boat or taking a detour to reach the other side. But with the bridge, we can walk across just like on a road, and the experience is no different from walking on the road. So I think the purpose of RPC is reflected in these two aspects:

  • Hiding the difference between remote and local calls, making us feel like we are calling methods within the project.
  • Hiding the complexity of underlying network communication, allowing us to focus more on business logic.

RPC Communication Process #

Having understood what RPC is, let’s now talk about the communication process of the RPC framework to further understand RPC.

As mentioned earlier, RPC allows our applications to transparently perform remote procedure calls. The party initiating the call request is called the caller, while the party being called is called the service provider. To achieve this goal, we need to encapsulate the entire communication details in the RPC framework. So, what steps are involved in a complete RPC?

We already know that RPC is a remote procedure call that requires data transmission over the network. RPC is commonly used for data exchange between business systems and needs to ensure reliability, so RPC generally uses TCP for transmission. The commonly used HTTP protocol is also based on TCP.

Data for network transmission must be in binary form, but the input and output parameters of the caller are objects. Objects cannot be directly transmitted over the network, so they need to be converted into transportable binary format in advance, and the conversion algorithm must be reversible. This process is commonly known as “serialization”.

The caller continuously serializes the request parameters into binary format and sends them via TCP to the service provider. The service provider receives the binary data from the TCP channel. How can the service provider know where a request’s data ends and what type of request it is?

Here, let’s think about a highway with many exits. To make drivers clearly know which exit to take, the management department will install many signs along the road, indicating the next exit and how far it is. Coming back to the data packet recognition scenario, can’t we also establish some “signs” and indicate the type and length of the data packets on them, so that we can correctly parse the data? Indeed, we can, and we call the agreed content of the data format “protocol”. Most protocols are divided into two parts: the header and the message body. The header is usually used for identification, including protocol identification, data size, request type, serialization type, etc. The message body mainly contains the business parameter information and extension attributes of the request.

According to the protocol format, the service provider can correctly separate different requests from the binary data. Then, based on the request type and serialization type, the binary message body is reversed back into a request object. This process is called “deserialization”.

The service provider then finds the corresponding implementation class based on the deserialized request object, completes the actual method invocation, and then serializes the execution result and writes it back to the corresponding TCP channel. The caller receives the response data packet, deserializes it into a response object, completing one RPC call.

Do the above processes constitute a complete RPC?

In my opinion, something is missing. Because for developers, doing this requires mastering too many low-level details of RPC, such as manually writing code to construct requests, invoking serialization, and making network calls. The entire API is very unfriendly.

So, what can we do to simplify the API, abstract away the RPC details, and allow users to only focus on the business interface, calling remote methods like calling local ones?

If you are familiar with Spring, you must admire its AOP technology, which uses dynamic proxy technology to intercept and enhance methods through bytecode enhancement, in order to add additional processing logic. In fact, this technology can also be applied to the RPC scenario to solve the problem we just faced.

The service provider declares the business interface, and in the caller’s program, the RPC framework dynamically generates a proxy implementation class based on the called service interface, and injects it into the relevant business logic that declares this interface using dependency injection and other techniques. This proxy implementation class intercepts all method calls and completes a complete set of remote calls in the provided method processing logic, returning the results of the remote calls to the caller. In this way, the caller can call remote methods just like local interfaces.

With this, a simplified version of the RPC framework is implemented. I have drawn the entire process for your reference:

The Position of RPC in Architecture #

We have talked a lot about RPC, but where does it exactly stand in an architecture?

As mentioned earlier, RPC is a way to solve inter-application communication. Whether it is in a large-scale distributed application system or in a small or medium-sized system, the application architecture will eventually evolve from a monolithic architecture to a microservices architecture. The entire application system will be divided into multiple applications with different functions, and they will be deployed on different servers. The applications communicate with each other through RPC. It can be said that RPC corresponds to the entire distributed application system, just like the “meridians” in traditional medicine.

So, what would our development process be like without RPC?

All the functional logic would be piled up in one big project. When you need to modify a line of code, it may take 2 minutes to compile and 5 minutes to run and verify the results. It can be quite frustrating, right? Furthermore, in a team with multiple developers, if someone changes the interface definition, you won’t even pass the compilation, and the system will directly report an error. This results in a very low development efficiency for the entire team. When we are preparing to release a new version, it is also difficult for QA to evaluate the scope of testing. For safety reasons, we can only perform regression testing for all the functionality, which extends the overall cycle for releasing new features.

Whether you are a developer or an architect, I believe we all cannot accept such a system architecture. So, how can we solve this problem?

We would first think of using the “divide and conquer” approach to split the system, but how can we maintain the same calling method as before after the split? We certainly cannot rewrite all the code just because of architecture upgrades.

An RPC framework can help us solve the communication problem after system splitting and allow us to call remote methods as easily as local ones. By using RPC, we can not only easily evolve the application architecture from a monolithic to a microservices architecture, but also solve issues such as low development efficiency and system coupling in the actual development process. This makes our system architecture clearer, more robust, and enhances application operability.

Of course, RPC is not only used for solving communication problems. It is also used in many other scenarios, such as sending messages to a message queue, distributed caching, and accessing databases. The following diagram shows an application architecture I previously developed:

In this application, I used a message queue (MQ) to handle asynchronous processes, Redis cache for hot data, and MySQL for persisting data. Moreover, my application also calls interfaces of another business system, which is also considered RPC for my application. The data stored in the distributed file system is used for the interaction between the different components, and RPC is used for the data exchange.

From this, it is clear that RPC is something we frequently encounter in our daily development work. It’s just been wrapped in various frameworks, so we rarely realize that it is RPC, making RPC the “familiar stranger.” Now, looking back, when I said RPC is the “meridians” of the entire application system, it wasn’t an exaggeration, right? It is really necessary for us to learn RPC well, not only because it is the cornerstone of building complex systems, but also because it is a tool to enhance our own understanding.

Summary #

In this lecture, I mainly talked about the principle of RPC. RPC provides a transparent invocation mechanism that allows users to distinguish between local and remote calls without explicitly doing so. Although RPC can help developers shield the difference between remote and local calls, there are still many differences in usage, such as:

  • How to handle business when the call times out?
  • When is the best scenario to use RPC?
  • When should compression be considered?

Whether you are a junior developer or a senior developer, RPC should be a topic that cannot be avoided in your daily development process. Therefore, as software developers, it is really necessary for us to understand the implementation details of RPC in detail. Only in this way can we better use RPC in our daily work.

Reflection after class #

  1. Where have you used RPC in your application?
  2. In your opinion, what issues should be noted during the use of RPC?

Feel free to leave comments and share your thoughts and questions with me. You are also welcome to share this article with your friends and invite them to join the learning process. See you in the next class!