Interview killer's Dubbo service call process

Like it and look again, get into a habit, search on WeChat [ third prince Ao Bing ] to follow this programmer who likes to write feelings.

This article has been included on GitHub https://github.com/JavaFamily , with the complete test sites, materials and my series of articles for interviews with major companies.

Preface

I have already taken you through the two processes of service exposure and service introduction, and these two processes are for the invocation of the service. Today, C will take you through the Dubbo service invocation process.

After reading today's service invocation process, the basic Dubbocore processes are completely connected together. In my mind, there should be the concept of Dubbo's overall operation. This system has been established, and I will have a further understanding of RPC.

Not much to say, let's go directly to the topic!

Simply think about the general process

Before analyzing Dubbothe service invocation process, let's first think about what steps need to go through if we let us implement it ourselves.

First of all, we already know the address of the remote service, and then what we have to do is to inform the remote service of the specific information about the method we want to call, and let the remote service parse the information .

Then find the corresponding implementation class based on this information, and then make a call. After the call is over, return the same way, and then the client parses the response and returns.

Call specific information

What should be included in the specific information that the client tells the server?

First of all, the client must tell which interface of the server is to be called. Of course, it also needs the method name, method parameter type, method parameter value, and there may be multiple versions, so you have to bring the version number.

From these few parameters, the server can clearly know which method the client wants to call, and can make precise calls!

Then assemble the response and return it. I will post an example of the actual call request object here.

Data is the data I mentioned, and the others are framed, including protocol version, calling method, etc. This will be analyzed below.

At this point, everyone knows the general meaning. It is an ordinary remote call, telling the request parameters, and then the server parses the parameters to find the corresponding implementation call, and then returns.

Landing call flow

The above is the imagined call flow, the actual landing call flow is not so simple.

First of all, remote calls need to define a protocol, that is, mutually agree on what language we want to speak , so that both parties can understand it.

For example, I can speak English and Chinese. You can also speak English and Chinese. We have to make an agreement to choose a language. For example, we will use Chinese to talk. Some people say something wrong. I can understand the English with your Chinese. .

That's because your brain is very smart, it can intelligently recognize the language of communication, but the computer is not, you think about your code to write print 1, can it print 2?

That is, the computer is rigid, and our program tells it what to do, and it will do it bluntly.

Need an agreement

Therefore, both parties must first define an agreement so that the computer can parse out the correct information .

Three common forms of agreement

The application layer generally has three types of protocol forms: fixed length form, special character partition form, and header+body form.

Fixed-length format : refers to the length of the protocol is fixed, for example, 100 bytes is a protocol unit, then the parsing starts after 100 bytes are read.

The advantage is that it is more efficient, and reads a certain length without thinking.

The disadvantage is that it is rigid. The length can only be fixed each time, and it cannot exceed the limited length. If it is short, it has to be filled. It is not suitable in the RPC scene. Who knows how long the parameters should be? .

Special character separation form : In fact, it is to define a special terminator, and judge the end of a protocol unit based on the special terminator, such as a newline character.

The advantage of this protocol is that the length is free. Anyway, it is truncated according to the special characters. The disadvantage is that it needs to be read until a complete protocol unit is read. Then the analysis can be started. If the special character is mixed in the transmitted data, an error will occur.

The form of header+body : that is, the header is of fixed length, and then the length of the body will be filled in the header, and the length of the body is not fixed, so the scalability is better. You can parse the header first, and then get it according to the header The len of the body then parses the body.

The dubbo protocol belongs to the form of header+body, and also has a special character 0xdabb, which is used to solve the problem of TCP network sticky packets.

Dubbo Agreement

There are many protocols supported by Dubbo. Let's briefly analyze the Dubbo protocol.

The protocol is divided into a protocol header and a protocol body. It can be seen that the 16-byte header mainly carries the magic number, which is the 0xdabb mentioned earlier, and then some requested settings, the length of the message body, and so on.

After 16 bytes is the protocol body, including protocol version, interface name, interface version, method name, etc.

In fact, the agreement is very important, because a lot of information can be learned from it, and only when you understand the content of the agreement can you understand what the encoder and decoder are doing. I will take a screenshot of the official website explaining the agreement.

Need to agree on the serializer

The network is transmitted in the form of a byte stream . Compared with our object, our object is multi-dimensional, while the byte stream is one-dimensional. We need to compress our object into a one-dimensional byte stream and transmit it to the right end.

Then the opposite end deserializes these byte streams into objects.

Serialization protocol

In fact, from the protocol in the figure above, we can see that Dubbo supports many kinds of serialization. I will not analyze each protocol in detail, but will roughly analyze the type of serialization, which will never change.

Serialization is roughly divided into two categories, one is character type and the other is binary stream.

The representatives of the character type are XML and JSON. The advantage of the character type is that it is easy to debug. It is friendly to people. We can know which parameter the field corresponds to at a glance.

The disadvantage is that the transmission efficiency is low, and there are many redundant things, such as JSON brackets. For network transmission, the transmission time becomes longer and the occupied bandwidth becomes larger.

There is also a big category is the binary stream type , this type is friendly to the machine, its data is more compact, so the number of bytes occupied is smaller and the transmission is faster.

The disadvantage is that it is difficult to debug and cannot be recognized by the naked eye, and special tools must be used for conversion.

The deeper level will not go deep. There are still many ways to serialize, and I will talk about it later when I have the opportunity.

Dubbo uses the hessian2 serialization protocol by default.

Therefore, the actual landing needs to first agree on the agreement, and then select the serialization method to construct the request and send it.

Rough call flow chart

Let's take a look at the picture on the official website.

A brief description is that the client initiates the call. The proxy class is actually called. The proxy class ultimately calls the Client (default Netty). The protocol header needs to be constructed, and then the Java object is serialized to generate the protocol body, and then the network is called for transmission.

Server-side NettyServerafter receiving the request, distributed to the thread pool business, the specific implementation method is called by a thread business.

But this is not enough, because Dubbo is a production-level RPC framework, it needs to be more secure and stable.

Detailed call flow

I have analyzed that the client also needs to serialize the construction request. In order to make the figure more prominent, this step is omitted. Of course, there is the step of responding back. For the time being, it is understood as returning to the original path. The analysis will be done later. .

It can be seen that the production level has to be stable, so there are often multiple servers, and multiple server services will have multiple Invokers. Finally, route filtering is required, and then one Invoker is selected for invocation through the load balancing mechanism.

Of course, Cluster also has fault tolerance mechanisms, including retries and so on.

Netty request will first reach an I / O read and write thread pool and, optionally, serialization and deserialization, can be decode.in.iocontrolled, and then processed after deserialize thread pool service, find the corresponding Invoker call.

Calling process-client source code analysis

The client calls the code.

String hello = demoService.sayHello("world"); 

Specific call interface calls the proxy class generation, and generates a proxy class RpcInvocationobject call MockClusterInvoker#invokemethod.

The RpcInvocation generated at this time is shown in the figure below, including method name, parameter class and parameter value.

Then we look at the MockClusterInvoker#invoke code.

It can be seen that it is to judge whether there is a mock in the configuration. If it is mock, it will not be analyzed. Let's take a look at the implementation of this.invoker.invoke, which actually calls AbstractClusterInvoker#invoker.

Template method

This is actually one of the very common design patterns, the template method. If you often look at the source code, you know that this design pattern is really too common.

The template method is actually to define the execution skeleton of the code in the abstract class, and then delay the specific implementation to the subclass, and the subclass customizes the personalized implementation , which means that the steps can be modified without changing the overall execution steps. The realization of, reduces the repetitive code, is also conducive to expansion, in line with the principle of opening and closing.

In the code that is doInvoketo be implemented by subclasses, some of the steps above are each subclass must go, it is pumped to an abstract class.

Routing and load balancing get Invoker

Let's look at that again list(invocation). In fact, it is to find Invoker by method name, and then filter the service route. There is also another MockInvoker.

Then take these Invokers for another wave of loadbalance selections, and get an Invoker. We use the default FailoverClusterInvokermethod of fault tolerance, which is the automatic switching of failures. In fact, routing, clustering, and load balancing are independent modules. If you expand it, it is still There are a lot of content, so I need to start another article, this article will use them as black boxes first.

To summarize a little, FailoverClusterInvoker gets the Invoker list returned by Directory, and after routing, it will let LoadBalance select an Invoker from the Invoker list .

Finally, the FailoverClusterInvokerparameters will be passed to the invoke method of the selected Invoker instance to make a real remote call. Let's briefly look at FailoverClusterInvoker#doInvoke. In order to highlight the key points, I deleted many methods.

The invoke that initiates the call is to invoke the invoke in the abstract class and then the doInvoker of the subclass. The method in the abstract class is very simple and I won't show it. The effect is not significant. Just look at the doInvoke method of the subclass DubboInvoker.

Three ways to call

From the code above, we can see that there are three types of calls, namely oneway, asynchronous, and synchronous.

Oneway is still very common, that is, when you don’t care whether your request is sent successfully, use the oneway method to send it. This method consumes the least amount of money. You don’t need to remember or care about anything.

Asynchronous calls , in fact, Dubbo is naturally asynchronous. You can see that the client will get a ResponseFuture after sending the request, and then wrap the future into the context, so that the user can get the future from the context, and then the user can do something After the wave operation, call future.get to wait for the result.

Synchronous call , this is our most commonly used, that is, the Dubbo framework helps us to transfer from asynchronous to synchronization. From the code, we can see that it is called in the Dubbo source code future.get, so the user feels that I block after calling the method of this interface Live, you have to wait for the result to return before returning, so it is synchronized.

It can be seen that Dubbo is asynchronous in nature. The reason why there is synchronization is because the framework helps us turn it around. The difference between synchronization and asynchronous is actually whether future.getthe user code is called or the framework code is called .

Going back to the source code, the currentClient.request source code is as follows is to assemble the request, construct a future, and then call NettyClient to send the request.

Let us look at DefaultFuturethe inside, you have not thought about a problem, because it is asynchronous, then the future holds after, such as how to respond back to find the corresponding future it?

Here is the secret! Is to use a unique ID.

You can see that Request will generate a globally unique ID, and then the future will internally store itself and the ID in a ConcurrentHashMap. After this ID is sent to the server, the server will also return this ID, so that the corresponding future can be found in ConcurrentHashMap through this ID, so that the entire connection is correct and complete!

Let's take a look at the code that finally received the response, it should be very clear.

Let's take a look at the next response message:

Response [id=14, version=null, status=20, event=false, error=null, result=RpcResult [result=Hello world, response from provider: 192.168.1.17:20881, exception=null]]

See this ID you will eventually call the DefaultFuture#receivedmethod.

In order to make everyone more clear, I will draw another picture:

At this point, almost the client calling the main process is very clear, in fact, there are many details, and I will talk about it in the following article, otherwise it will be too messy and complicated.

The call chain to initiate the request is shown in the figure below:

The call chain for processing the request response is shown in the following figure:

Calling process-server-side source code analysis

After receiving the request, the server will parse the request and get the message. There are five distribution strategies for this message:

The default is all, that is, all messages are dispatched to the business thread pool, let's look at the implementation of AllChannelHandler.

It is to encapsulate the message into a ChannelEventRunnable and throw it into the business thread pool for execution. ChannelEventRunnable will call the corresponding processing method according to the ChannelState. Here is ChannelState.RECEIVED, so the call handler.receivedwill eventually call HeaderExchangeHandler#handleRequest. Let’s take a look at this code.

Have you seen this wave of key points, the constructed response is first stuffed with the requested ID, and let's take a look at what this reply does.

The final call is already clear. In fact, it will call a proxy class generated by Javassist, which contains the real implementation class. We have analyzed it before and will not go into it here. Let’s take a look at getInvokerthis method again and see how it is based The requested information finds the corresponding invoker.

The key is the serviceKey. Remember that the previous service exposure encapsulated the invoker into an exporter and then constructed a serviceKey and stored it and the exporter in the exporterMap. This map will work at this time!

This Key looks like this:

Find the invoker, finally call the specific method of the implementation class and then return to respond to the entire process is over, I will add the previous figure.

to sum up

Today's call process should be almost the same if I summarize it again.

First, the client calls a certain method of the interface, which actually calls the proxy class. The proxy class will obtain a bunch of invokers from the directory through the cluster (if there are a bunch of them), and then filter the router (see the configuration will also add mockInvoker to use Service degradation), and then get loadBalance through SPI for a wave of load balancing.

It should be emphasized here that the default cluster is FailoverCluster, which will perform fault-tolerant retry processing, and we will analyze it in detail later.

Now we have obtained the invoker corresponding to the remote service to be called. At this time, we construct the request header according to the specific protocol, then serialize the parameters according to the specific serialization protocol and then construct the structure into the request body, and then initiate the remote call through NettyClient.

After receiving the request, the server NettyServer obtains the information according to the protocol and deserializes it into an object, and then distributes the message according to the distribution strategy, the default is All, and throws it to the business thread pool.

The business thread will judge according to the message type and then get the serviceKey to get the corresponding Invoker from the exporterMap generated by the previous service exposure, and then call the real implementation class.

The result is finally returned, because both the request and the response have a unified ID. The client finds the stored Future according to the response ID, then inserts the response and wakes up the thread waiting for the future to complete the whole process of a remote call.

And I also talked a little about the template method design pattern. Of course, there are many design patterns hidden in it, such as the chain of responsibility, decorators, etc., without deliberately saying that the source code is too common and basically everywhere.

Talk

After passing today's article, I believe that everyone has a better understanding of RPC calls. The previous service exposure and service introduction are all to complete remote calls.

For the codec part, I just talked about the specific protocol, and did not analyze it from the perspective of source code because that part of the code is actually written and obtained according to the protocol, which is relatively rigid, and interested students can study by themselves, and some are about Buffer The operation still needs a certain foundation.

The main line is basically finished, but there is no detailed analysis of routing, clustering and other mechanisms. This part is also very important. Compared with the production environment, it is basically cluster deployment. There is no stunned green. So Dubbo is on this. The support from this aspect is also very important and will be analyzed later.

Talk

In addition, Ao Bing compiled his interview articles into an e-book with 1,630 pages! The content is as follows, as well as the interview questions and resume templates that I summarized during the review, which are now given to everyone for free.

Link: https://pan.baidu.com/s/1ZQEKJBgtYle3v-1LimcSwg Password:wjk6

This is Ao Bing. The more you know, the more you don’t . Thank you all for your praise , favorites and comments . See you in the next issue!


The article is continuously updated. You can search for " San Tai Zi Ao Bing " on WeChat to read it for the first time, and reply to [ Information ] I have prepared the interview information and resume template of the first-line manufacturers. This article has been included on GitHub https://github.com/JavaFamily , There are complete interview test sites with big factories, welcome to Star.

Guess you like

Origin blog.csdn.net/qq_35190492/article/details/108570109