I’ll write the 1 out front

In this series of articles, we will learn and understand gRPC from the source level.

The plan for this series of articles goes something like this: We’ll start with the client side and work our way up the call path to the server side, learning at a module-level, thinking about what problem this module is trying to solve, and then thinking about how gRPC should solve that problem. After analyzing this part of the architectural design, we will look at the specific code implementation in an upcoming article.

As a result, this series of articles will not post large chunks of code and then comment on them, as previous source analysis did. Not only does this make reading expensive, it makes it difficult to learn anything beyond the code implementation.

We’ll start with the client side and work our way up the call path to the server side.

2 What is RPC

Before reading the source code of gRPC, we first think about implementing an RPC framework, what kind of functionality should be provided?

In our last article, we saw how gRPC can be used. To put it simply, for the same method, the specific logic is implemented on the server side and the call is initiated on the client side to achieve “remote procedure call”.

So, how do we do this?

So we can easily speculate that both the client and the server, in the way we call behind must also encapsulate a set of complex logic, is responsible for the client calls the “send” to the server, the server also encapsulate a set of complex logic, is responsible for receiving the request of the client to send to come over, and choose corresponding method according to the data received, After execution, the result is “returned” to the client.

And so we’re going to speculate, what’s in this complicated logic?

Let’s take the client as an example: First, we need to establish a connection with the server. When we call a remote method, we need to let the server know which method the client is calling, which parameters, etc., which means we need to design a protocol that carries the above information. Finally, we’re going to stuff our data into this protocol, encode it in binary format, and stuff it into the network.

For the server, it is the same. After receiving binary data from network IO, it needs to decode, and then according to the decoded data, it knows the method name and parameters to be called, and sends the result back to the client after executing the corresponding method.

Is that enough?

Not enough. We need a way to encapsulate the above logic so that we don’t have to write a bunch of code repetitions every time we call it. That is, our developer doesn’t need to know the low-level invocation details, he just needs to define and invoke methods, and the framework does the rest.

At this point, we have implemented a basic RPC framework.

But you may have a question, if the RPC framework only provides a communication function, then what is the point of its existence?

If it’s just for communication, we don’t have to develop a new framework, we can use RESTful apis, and you can even stuff data into TCP packets.

The answer is that although RPC is called remote procedure call, the RPC framework is not only capable of realizing communication between services, but also provides some functions of service governance, load balancing, flow control and so on.

So when we talk about RPC frameworks, we usually say that they provide a complete solution with remote procedure calls at the core.

3 How to implement gRPC

In the last section, we talked about what an RPC framework should provide. In this section, let’s talk about what gRPC does.

3.1 Connection Management

To make connections more reliable and efficient, gRPC needs to manage connections.

Consider a scenario where the gRPC server expands from a single server to a cluster due to the expansion of the company size and traffic. At this point, our client needs to call one of the server methods, so which machine does the client need to establish a connection to send data?

If we break the problem down more specifically, the following are the problems that need to be solved:

  • Assuming there are many machines in the cluster, how can we tell the client the IP :port of each server machine?

  • Suppose we add or subtract some gRPC servers, how does the client update the IP :port list it maintains?

  • If the client is currently requesting a server with multiple IP addresses: ports, which connection should the client send data to?

These problems can be boiled down to how gRPC solves the problems of service registration, service discovery and load balancing.

However,gRPCThere is no such thing asSpring Cloud,DubboSuch as the framework of service registration, service discovery functions.

I think the reason gRPC is doing this is to provide more flexible service discovery and load balancing.

3.2 Resolver

ResolverCalled a parser, it can parse the “names that match certain rules” passed in by the client into a list of IP addresses.

Suppose you define an address format: aaa:///bbb-project/ CCC-srv

The Resolver then resolves this address into several IP addresses :port, which represents the addresses of all the machines that provide the CCC-SRV service cluster.

That’s what a Resolver does.

How does a Resolver parse? In other words, how does a Resolver input an IP address and output a string of IP addresses?

This part of the work needs to be done by the users themselves.

GRPC provides plug-in Resolver function. It selects a Resolver that can resolve AAA according to aaa:///bbb-project/ CCC-srV passed in by the user and resolves it to obtain the IP :port list.

3.3 Balancer

A Balancer is a load Balancer that selects one of the addresses resolved by the Resolver to establish a connection.

It is up to the user to write the LB logic.

That said, gRPC implements the basic logic, but also provides powerful plug-in programming capabilities, leaving much of the action up to the developer.

However, a lot of flexibility is matched by a complex code structure, and looking directly at the source can be confusing. So in this article we will first introduce a design logic of the whole, and in the next article we will talk about the details.

3.4 Wrapper

GRPC Resolver and Balancer are both customizable. We can define a variety of resolvers and balancers ourselves to meet the needs of different scenarios.

While this increases code complexity, it makes gRPC more flexible and capable of supporting complex scenarios.

So how do you implement plug-in programming?

The answer is to use the decorator pattern.

A Decorator is also called a Wrapper pattern. GOF’s definition in His book Design Patterns is to dynamically add additional responsibilities to an object.

The decorator pattern, which dynamically adds additional responsibilities to an object, is more flexible in terms of adding functionality than subclassing. It wraps the real object by creating a wrapper object, a decoration.

This may sound a little abstract, but let’s go straight to the picture above:

Create a resolver interface and design some concrete resolver implementation classes:

Then we need a resolver wrapper that contains the actual Resolver.

When our gRPC needs to call the ResolverNow method, it only needs toresolverWrapperIn theResolve()Method in which to call the realresoleNow()Logic:Once you understand this design pattern,gRPC ClientYou’ll understand more than half of the connection code.

At this point, gRPC’s management of the connection ends.

4 summarizes

Finally, let’s take a look at how gRPC manages connections.

When establishing a connection for the first time, the gRPC invokes the Resolver corresponding to the server address to resolve all the server addresses that can provide services. Then, through the specified Balancer, one of the addresses is selected to establish a connection.

If a connection has been established, a coroutine exists in the Resolver to monitor the status of the service. If there is a new online or offline service, the Resolver will perform address resolution to obtain a new server address set, and then select an address to establish the connection through Balancer.

With this article in mind, it might be easier to read the implementation code. We’ll examine this part of the code in the next article.

As for the establishment of the connection, the logic is also complicated, which we will continue to analyze in a later article.

Write in the last

First of all, thank you for being here!

This article has a strong subjective judgment, because the author is uneducated, there may be misunderstandings about the design idea of gRPC. If you think what I said is wrong, please do not hesitate to comment, thank you!

In the next article, I’ll try to keep the code as simple as possible, so stay tuned.

Finally, if you have any questions, please leave a comment or find me on the official account “Red Chicken fungus”.

Thanks again for reading 🙂