Dubbo notes ⑪: consumer call process

Posted by pdaoust on Tue, 21 Dec 2021 17:10:56 +0100

1, Foreword

This series is personal Dubbo learning notes, which is based on in-depth analysis of Apache Dubbo core technology insider, and process reference Official source code analysis article , for personal note taking only. This analysis is based on dubbo2 In version 7.0, due to the limitations of personal understanding, if there are inevitable errors in the text, thank you for your correction.

Series address: Dubbo source code analysis: Complete Collection

In the previous article, we know that the RefProxy structure obtained by the consumer is as follows

In other words, when the consumer initiates a call, the sequence diagram is as follows (Figure source: in-depth analysis of Apache Dubbo core technology):
It should be noted that the invokerdelegeete in the figure is the list of invokers in the Directory and the delegate class of invokers in the RegistryDirectory#toInvokers method. As follows:

Let's make a specific analysis according to the call sequence in the sequence diagram:

2, MockClusterInvoker

MockClusterInvoker implements local mocks. The local service mock of the service consumer is mainly used for local testing. When the service provider is unavailable, the local mock service can simulate the service provider to let the service consumer test its functions without initiating remote calls.

To realize the mock function, the client must first implement the mock implementation class of the server. It should be noted that the mock implementation class must conform to the interface package name The class name is in mock format. It should be noted that the remote call will be initiated before the mock service implementation class mock() method is executed. When the remote service call fails, the mock function will be degraded.

The specific analysis has been completed. If necessary, please refer to: Dubbo notes derivation ⑥: local Mock and service degradation

3, FailoverClusterInvoker

Although the FailoverClusterInvoker class is drawn on the sequence diagram, the entire Dubbo cluster fault tolerance function is actually completed in this class. Dubbo fault tolerance includes the following components:

  1. Cluster & Cluster Invoker: there are two concepts in Dubbo cluster fault tolerance: cluster interface and Cluster Invoker, which are different. Cluster is an interface, and Cluster Invoker is an Invoker. The selection logic of the service provider and the processing logic after the remote call fails are encapsulated in the Cluster Invoker. The cluster interface and related implementation classes are simple and only used to generate cluster invokers. Simply put, cluster is used to create cluster invokers, which correspond to each other one by one. The role of cluster and Cluster Invoker is to select the fault-tolerant strategy when consumers make service calls, such as retry, throw an exception, or return an empty result set after service calls fail.

  2. Directory: directory is the service directory. The service directory stores some information related to the service provider. Through the service directory, the service consumer can obtain the information of the service provider, such as ip, port, service protocol, etc. Through this information, service consumers can make remote calls through clients such as Netty. In fact, after obtaining the service configuration information of the registry, the service directory will generate an Invoker object for each configuration information and store the Invoker object, which is the object finally held by the service directory. Simply put, the directory stores a collection of service providers that can currently provide services. When consumers make service calls, they will select a service provider from the directory to provide services according to some rules.

  3. Router: the service route contains a routing rule, which determines the call target of the service consumer, that is, specifies which service providers the service consumer can call. In the process of refreshing the Invoker list, the service Directory will route the service through the router to filter out the service providers that meet the routing rules. Simply put, the router has formulated some service rules. The service provider list in the Directory must meet the router rules before it can be used as a candidate service provider list.

  4. LoadBalance: when the service provider is a cluster, in order to avoid that a large number of requests are always concentrated on one or several service provider machines, so that the load of these machines is very high and even the services are unavailable, a certain load balancing strategy needs to be made. Dubbo provides a variety of balancing strategies. By default, it is random, that is, the services of one service provider are called randomly at a time. Simply put, LoadBalance establishes a policy that allows requests to be distributed to the service provider's machine according to certain rules (random, hash, etc.).

In this link, Dubbo's cluster fault tolerance function is completed. The fault tolerance of the whole cluster can be summarized as follows:

  1. The request goes through ClusterInvoker#invoke, where the list of service providers supporting this call will be obtained from the registry through the Directory component.
  2. All service providers obtained in the Directory will reject those that do not comply with the routing rules through the Router#route, and then return the remaining service providers to the ClusterInvoker.
  3. After getting the list of service providers returned from the Directory, ClusterInvoker initializes the load balancing policy according to the user parameters, and uses the selected load balancing policy to select a provider from the list of service providers returned in the previous step to provide services for this call.

For detailed implementation of specific components, please refer to: Dubbo notes ⑫: overview of Dubbo cluster components

4, ProtocolFilterWrapper

ProtocolFilterWrapper is for Dubbo org apache. dubbo. rpc. Filter processing. For Servlet Filter, you can filter the requests of servlets, but you can't filter the requests of RPC calls. At this time, Dubbo's org.com is required apache. dubbo. rpc. Filter.

If necessary:

  1. stay Dubbo notes derivative ③: ProtocolWrapper There is analysis of ProtocolFilterWrapper in.
  2. stay Dubbo notes ⑰: detailed explanation of Dubbo Filter Analysis of each Filter is provided in.

5, DubboInvoker

DubboInvoker#doInvoke is the place where the call is officially initiated. The specific implementation is as follows:

    @Override
    protected Result doInvoke(final Invocation invocation) throws Throwable {
        RpcInvocation inv = (RpcInvocation) invocation;
       	// ...  Other processing
        try {
        	// Whether it is asynchronous or not, judge whether your condition is async attribute in RpcInvocation
            boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
            // Whether to asynchronous Future, the judgment condition is Future in RpcInvocation_ ReturnType property
            boolean isAsyncFuture = RpcUtils.isReturnTypeFuture(inv);
            // Do you need to return a response
            boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
            int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
            // Synchronization & & does not require a return value
            if (isOneway) {
                boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
                // Initiate synchronization request
                currentClient.send(inv, isSent);
                // The setting context Future is empty
                RpcContext.getContext().setFuture(null);
                return new RpcResult();
            } else if (isAsync) {
            	/******* Asynchronous call*********/
            	// 1. Make asynchronous call
                ResponseFuture future = currentClient.request(inv, timeout);
                // For compatibility
                // 2. Use FutureAdapter to package future
                FutureAdapter<Object> futureAdapter = new FutureAdapter<>(future);
                // Set the adapter to the context.
                RpcContext.getContext().setFuture(futureAdapter);

                Result result;
                // 3. Processing of returned results. If the method return type is completable future, wrap the result as AsyncRpcResult. Otherwise, SimpleAsyncRpcResult is returned
                if (isAsyncFuture) {
                    // register resultCallback, sometimes we need the async result being processed by the filter chain.
                    // 3.1 if the return type of asynchronous request is completable future, wrap the result as AsyncRpcResult
                    result = new AsyncRpcResult(futureAdapter, futureAdapter.getResultFuture(), false);
                } else {
                	// 3.2 if the return type of asynchronous request is not completable future, wrap the result as SimpleAsyncRpcResult
                    result = new SimpleAsyncRpcResult(futureAdapter, futureAdapter.getResultFuture(), false);
                }
                return result;
                /**********************/
            } else {
            	// In other cases, the return value is required for synchronization
                RpcContext.getContext().setFuture(null);
                // Directly block the asynchronous call result of the thread. DefaultFuture#get is called here
                return (Result) currentClient.request(inv, timeout).get();
            }
        }
        ....
    }

The above code comments are very clear. For the process of asynchronous invocation by consumers, please refer to Dubbo notes ⑳: asynchronous calls by consumers.