Dubbo Source Parsing - Network Calls

Posted by clip on Sun, 15 Dec 2019 06:04:43 +0100

Dubbo Network Call

background

We know that the general flow of Dubbo remote calls (consumer processes) is as follows:

  • Get a list of invoker s for this method from Dirctory
  • A list of invoker s that meet the criteria is obtained by filtering router routes
  • invoker called with Cluster fault tolerance
  • Final invoker filtered by loadBalance
  • filter chain passing through the consumer side
  • Network Request and Serialization
  • .....The provider executes the request and returns the result
  • User Threads Get Results

This article describes several ways to make network requests and how user threads get the final result.

Network Call Method

Dubbo uses Netty as its underlying communication framework. Netty is a NIO-based communication framework. How does Dubbo use Netty for network communication?

Dubbo is an RPC communication framework that provides inter-process communication and provides three API calls when using Dubbo protocol+Netty as the transport layer:

  1. Synchronization interface
  2. Asynchronous with callback interface
  3. Asynchronous without callback interface

The parameters isOneway and isAsync are used in Dubbo to control how the call is invoked:

  • isOneway=true means asynchronous without callback
  • isAsync=true means asynchronous with callback
  • Neither of the above is satisfied, using a synchronous API

Synchronization interface

concept

Synchronization interface is applicable in most environments. The communication mode is simple and reliable. Client initiates calls, waits for service to process, and the results of calls are returned synchronously.

In this way, it is best suited for high throughput, high performance (fast response time) service interface scenarios, which can reduce the additional cost of asynchronization and facilitate client consistency assurance.

Source code

In the case of synchronization, the client initiates the request and blocks the response waiting for the server through the get() method:

RpcContext.getContext().setFuture(null);
return (Result) currentClient.request(inv, timeout).get();

// Code location: org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker#doInvoke

We enter the get() method:

public Object get(int timeout) throws RemotingException {
  if (timeout <= 0) {
    timeout = Constants.DEFAULT_TIMEOUT;
  }

  // If no response is received, make a circular, circular judgment
  if (!isDone()) {

    long start = System.currentTimeMillis();
    lock.lock();
    try {
      while (!isDone()) {
        // Blocked in waiting queue, waiting to wake up
        done.await(timeout, TimeUnit.MILLISECONDS);
        if (isDone() || System.currentTimeMillis() - start > timeout) {
          break;
        }
      }
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    } finally {
      lock.unlock();
    }

    if (!isDone()) {
      throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
    }
  }

  // Get results from response object and return
  return returnFromResponse();
}

// Code location: org.apache.dubbo.remoting.exchange.support.DefaultFuture#get(int)

When will the user thread wake up?

private void doReceived(Response res) {
  lock.lock();
  try {
    response = res;
    if (done != null) {
      // Wake up waiting client threads when a response is received
      done.signal();
    }
  } finally {
    lock.unlock();
  }
  if (callback != null) {
    invokeCallback(callback);
  }
}

// Code location: org.apache.dubbo.remoting.exchange.support.DefaultFuture#doReceived

Technological process

  • User Threads Initiate Network Requests
  • User thread calls ResponseFuture.get() method into blocking state
  • Provider executes request and returns result
  • Wake User Threads
  • The user thread returns from the await() method and gets the result

Asynchronous with callback interface

concept

Asynchronous with callback interface, used for long task processing time, client application threads do not want to block waiting, but in order to improve their own processing power, the server can asynchronously notify application threads when processing is complete.This can greatly improve the throughput of the client and avoid dragging the client to death due to time-consuming issues on the server side.

actual combat

First set referenceConfig to async parameter to asynchronous call:

<dubbo:reference
            id="demoService"
            interface="com.huang.yuan.api.service.DemoService"
            version="1.0"
            async="true"   
            timeout="1000000">
</dubbo:reference>

The provider code is as follows:

@Override
public ModelResult<String> test(String param) {

  spLogger.warn("Remote method executed, go to sleep");

  try {
    Thread.sleep(1000);
  } catch (Exception e) {
    e.printStackTrace();
  }

  spLogger.warn("The remote method is over");

  return new ModelResult<>(param);
}

The consumer code is as follows:

public void test() throws Exception {
  ModelResult<String> modelResult = demoService.test("huangyuan");
  System.out.println("Get results immediately= " + modelResult);

  System.out.println("User thread does something else first...");

  Future future = RpcContext.getContext().getFuture();
  System.out.println("User threads pass through future Get results= " + future.get());
}

The results are as follows:

Source code

In the case of asynchronous requests, after the user thread initiates the request, place a Future in the RpcContext and return an empty result immediately.

ResponseFuture future = currentClient.request(inv, timeout);
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
// Code location: com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker#doInvoke

The user thread can do something else. When the user thread wants to get the result, it can call the Future.get() method to try to get the result. The code goes to com.alibaba.dubbo.remoting.exchange.support.DefaultFuture#get(int), and if the provider has not returned the result, the user thread enters the blocked state.

Similarly, after receiving the provider's results, call back com.alibaba.dubbo.remoting.exchange.support.DefaultFuture#doReceived, and the user thread can return from the blocked state to get the results.

Technological process

Refer to the illustration of the official website as follows:

  • 1. User Thread Call Interface
  • 2. Make network requests
  • 3. Set future to RpcContext context
  • 4. User thread gets future from RpcContext context
  • 5. User thread calls get() method to wait for result and enters blocking state
  • 6. Return response from remote call
  • 7. Wake up user threads to get results

You can see that although Dubbo currently makes asynchronous calls, it still needs to block the results synchronously. This problem is solved in apache dubbo through CompletableFuture. User threads really don't care when the results return. As long as Dubbo calls back to the user thread, the user thread can fetch the results.

Asynchronous without callback interface

concept

Asynchronous does not have a callback interface. In some scenarios, in order to further improve the throughput of the client, only one service-side call needs to be initiated, and the result of the call is not of concern. This communication can be used.

Generally, this option minimizes the performance penalty of remote calls without stringent data consistency or other compensation measures.

actual combat

The current configuration file for Dubbo does not seem to support this method of invocation, which can be used by customizing FIlter and overwriting the Dubbo parameters:

<dubbo:reference
  id="demoService"
  interface="com.huang.yuan.api.service.DemoService"
  version="1.0"
  timeout="1000000"
  filter="testFilter">  // Custom filter
</dubbo:reference>

Custom Filter s are as follows:

public class Filter2 implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        
        Map<String, String> attachments = invocation.getAttachments();

        // By using the parameter RETURN_KEY, the call does not require a return value
        attachments.put(Constants.RETURN_KEY, "false");

        return invoker.invoke(invocation);
    }
}

Output results:

Source code

Asynchronous invocation without callback interface, the source code is very simple, that is, after the request is made, an empty result is returned immediately

boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);
return new RpcResult();

Reference resources

Implementation principle of synchronous API in non-blocking communication

Topics: Java Dubbo network Netty Apache