24 How to Accommodate Multiple Rpc Protocols in an Online Environment

24 How to accommodate multiple RPC protocols in an online environment #

Hello, I am He Xiaofeng. In the previous lesson, we learned how to make RPC calls without interfaces, and the key is to understand the role of interface definitions in RPC. In addition to what we mentioned before, interface definitions are only used as metadata in the remaining process, and dynamic proxies are not a necessary step in RPC. Therefore, we can also complete RPC calls without interface definitions.

After reviewing the key points of the previous lesson, let’s get back on track and discuss today’s topic: how to support multiple RPC protocols in an online environment.

When you see this question, your first reaction might be, why would there be multiple protocols in a real environment? We have mentioned that RPC helps us abstract the network programming details and achieve the same experience as calling local methods. In plain terms, RPC is a tool that helps us achieve communication between applications during the development process without having to worry about the specific communication details.

Why support multiple protocols? #

Since the communication between applications is done through RPC, there are many tools that can be used for RPC communication, such as Web Service, Hessian, gRPC, etc. These different RPC frameworks have emerged with the development of Internet technology, and these RPC frameworks may be introduced into different projects to solve the communication problems between applications at different times. This leads to the existence of various RPC frameworks in our production environment.

Obviously, this haphazard use of RPC frameworks is not conducive to the management of the company’s technology stack. The most obvious characteristic is that the cost of maintaining RPC frameworks is increasing because each RPC framework requires dedicated personnel for upgrading and maintenance.

In order to solve some technical debt left over from the early days, we usually choose more advanced and easier-to-use tools to solve them. The governance of the chaotic RPC framework is also the same. In order to solve the difficulty of maintaining multiple RPC frameworks at the same time, we definitely hope to unify all RPC frameworks in our production environment with one RPC framework. This will not only reduce our maintenance cost, but also allow us to focus on improving one RPC framework.

Now that the goal is clear, how do we implement it?

You may say it is simple, we just need to transform all applications to use the new RPC framework, and then deploy all the transformed applications at the same time. If the team is small, this cliff-like update may indeed be the fastest method. However, if it is a large team, it is almost impossible to deploy all transformed applications at the same time, whether or not there are risks in this approach.

So for a multi-person team, is there a way to unify multiple RPC frameworks into one tool? Let’s first look at the difficulties that a multi-person team faces during the upgrade process. More team members mean more applications to maintain, and as the number of applications increases, the calling relationships between applications become more complex. In this case, if we simply replace the RPC framework currently used by any application with a new RPC framework, we need to make all callers of this application change to the new calling method.

Through this bottom-up rolling upgrade method, eventually all applications can be switched to a unified RPC framework. However, this upgrade method has certain limitations. First, it requires us to clearly analyze the calling relationships between various applications. Only in this way can we upgrade all applications to the new RPC framework step by step. Secondly, it requires that there are no mutual calling relationships between applications. The best situation is that the calling relationships between applications are like a tree with certain hierarchical relationships. But in reality, the calling relationships between our applications may have become a network structure, so it may be difficult to proceed with the upgrade in this way.

To solve the problems encountered in the above upgrade process, you may think of another solution, which is to not remove the original RPC framework during the application upgrade, but to simultaneously connect to the new RPC framework and let both RPC frameworks provide services at the same time. Then, after all applications have connected to the new RPC framework, gradually switch all applications to the new RPC framework. This can solve the above problems and allow all applications to upgrade to the unified RPC framework in an unordered manner.

The idea of simultaneously introducing a new RPC framework while keeping the original RPC usage unchanged can eventually allow all applications to be upgraded to the RPC framework we want to upgrade. However, for developers, the switching cost is still relatively high, and the entire process requires at least two deployments to completely switch the old RPC in the application to a new one.

Is there a better way to switch between new and old RPCs with just one deployment of an application? The key is to allow the new RPC to support multiple RPC protocols at the same time. When a caller switches to the new RPC, the caller and the service provider can use the new protocol for communication. When the caller still uses the old RPC for calling, the caller and the service provider continue to use the old protocol for communication. For the service provider, the request handling relationships are shown in the following diagram:

image

How to gracefully handle multiple protocols? #

To enable the new RPC to support multiple RPC calls at the same time, it is crucial to allow the new RPC to support multiple protocols simultaneously. How can this be achieved? In the [Lesson 02], we mentioned that the purpose of a protocol is to divide the binary data stream. Each protocol has a different data packet format, and each protocol starts with a protocol code, commonly known as a magic number.

When the RPC receives a data packet, we can first parse the magic number. Once we obtain the magic number, it becomes easy for us to identify the data format of the corresponding protocol, and then we can use the data format of the corresponding protocol to parse the received binary data packet.

The protocol parsing process is to convert a series of binary data into an internal RPC object. However, this object is generally protocol-dependent. To make it more convenient for the internal handling of the RPC, we usually convert this protocol-dependent object into an RPC object that is independent of the protocol. This is because in the RPC process, when the service provider receives the deserialized request, we need to find the implementation class of the corresponding interface based on the current request parameters to complete the actual method invocation. If the request parameter is related to the protocol, the subsequent processing logic of the entire RPC will become very complex.

After the actual method invocation is complete, the RPC also returns a protocol-independent general object. Therefore, when writing data back to the caller, we also need to complete an object conversion logic. However, this time, we are converting the general object back into a protocol-dependent object.

During the process of sending and receiving data packets, we use the two conversions to make the internal processing logic of the RPC protocol-independent, while ensuring that the data format received by the caller is the same as the data format of the original call request. The entire process is illustrated in the following diagram:

Summary #

In our daily development process, the most challenging task is not developing a new application from scratch, but upgrading an existing application from 70 points to 80 points through architectural improvements. This is because during the upgrade process, we not only need to consider the existing functional logic, but also the cost of switching to the new architecture. This requires us to consider how to smoothly upgrade the old application when designing the new architecture, just like supporting multiple protocols in RPC.

Supporting multiple protocols in RPC not only allows us to advance the upgrade of application RPC more confidently, but also lays a solid foundation for future expansion of new protocols in RPC. Therefore, when designing application architectures, we not only need to consider the completeness of application functionality, but also the operability of the application, as well as soft capabilities such as smooth upgradeability.

After-Class Reflection #

When supporting multiple protocols in RPC, a key point is the ability to identify different protocols and find the corresponding parsing logic based on different magic numbers. If there are many protocols in production, we need to pre-install various protocols in the RPC. However, using enumeration may lead to omissions. I wonder if you have any good solutions for this problem?

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