This article is more than one year old. Older articles may contain outdated content. Check that the information in the page has not become incorrect since its publication.
Implementing the full asynchronous programming based on Dubbo, which is a new feature introduced in version 2.7.0 after the enhancement of the existing asynchronous mode.This article first reviews the supported functions and existing problems of asynchronization in 2.6.x and earlier versions, and introduces the targeted enhancements based on CompletableFuture in version 2.7.0. Then, the use of enhanced asynchronous programming is elaborated through several examples. Finally, it summarizes the new problems brought by the introduction of asynchronous mode and corresponding solutions from Dubbo. By reading this article, it is easy to implement a fully asynchronous remote service call chain based on Dubbo 2.7.0+.
Dubbo Provides some asynchronous programming capabilities in 2.6.x and earlier versions, including Asynchronous Call, Parameter Callback and Event Notification on Consumer side. There are some brief introductions to the usage and Demo in the above document links.
But the current asynchronous method has the following problems:
Take the asynchronous method of Consumer side as an example:
or
From this simple example, we can see there are some inconveniences in use:
People who understand the evolution history of Future in Java should know that the Future used in Dubbo 2.6.x and earlier versions is introduced in Java 5, so there are some problems in function design.The CompletableFuture introduced in Java 8 further enriches the Future interface and solves these problems well.
Support for Java 8 has been upgraded in Dubbo 2.7.0, and Dubbo has enhanced the current asynchronous functionality based on CompletableFuture.
Now it supports direct definition of service interfaces that return CompletableFuture. Through these interfaces, we can implement asynchronous programming on both Consumer side and Provider side more naturally.
If you don’t want to define the return value of the interface as a Future object, or if there is a defined synchronization interface, you can additionally define an asynchronous interface and provide a method to return a Future object.
In this way, Provider can only implement the sayHi method. The Consumer can get a Future instance by directly calling sayHiAsync, and Dubbo framework will convert it to a call to the sayHi method on the Provider side automatically.
Providing an asynchronous method definition for each synchronization method can be inconvenient. Further, using Annotation Processor implementation in the Dubbo ecosystem can automatically generate asynchronous method definitions for us.
Similarly, if your original interface definition doesn’t return a Future object, the Provider side also provides a programming interface similar to the Async Servlet in Servlet 3.0 to support asynchronization : RpcContext.startAsync()
.
At the beginning of the method body, it starts asynchronization by running RpcContext.startAsync()
, and it starts a new thread to execute the business logic asynchronously. After the time-consuming operation is completed, the result is written back by asyncContext.write
.
RpcContext returns CompletableFuture directly.
All of the above enhancements are based on the compatibility with existing asynchronous programming, so asynchronous programs written based on 2.6.x versions can be successfully compiled without any modification.
Next, let’s illustrate how to implement a fully asynchronous Dubbo service call chain through a few examples.
CompletableFuture interface can be used both for a synchronous call and for an asynchronous call on Consumer or Provider side. This example implements asynchronous calls between Consumer and Provider sides. Code link dubbo-samples-async-original-future.
Interface definition
Note that the return type of this interface is CompletableFuture<String>
.
Provider Side
Implementation
We can see that the business code is switched to be executed in the new thread by supplyAsync, so the Provider side is asynchronous.
Config
The Config is the same as the original interface.
Consumer Side
The Config is the same as the original interface.
CompletableFuture<String> future = asyncService.sayHello("async call request");
It is convenient to return the Future instance, which implements the asynchronous service call on the Consumer side.
This example demonstrates how to implement the Consumer-side asynchronous service call using the Annotation Processor based on the original synchronous interface. Code link dubbo-samples-async-generated-future.
Interface definition
This is a generic definition of the Dubbo service interface. Note that add the @DubboAsync annotation when using Annotation Processor.
The above config is the Maven dependency that imports dubbo-async-processer processor. Developers who define interfaces (providing APIs) usually add the above dependencies to the project, so that when doing API packaging, the following interface definitions will be automatically generated in APIs:
Provider side
Consumer side
Note that the service interface uses GreetingsServiceAsync
In this way, we can use CompletableFuture<String> future = greetingsService.sayHiAsync("async call reqeust");
directly,and return CompletableFuture.
This example demonstrates how to implement the Provider-side asynchronous execution through AsyncContext based on the original synchronous interface. Code link dubbo-samples-async-provider.
Interface definition
Provider side
Note that adding async="true"
indicates that this is a service that starts the Provider-side execution asynchronously.
Consumer side
The following is a complete Filter chain for a normal Dubbo call.
After using the asynchronous call, since the asynchronous result is executed separately in the asynchronous thread, the Result passed through the second half of the Filter chain is null, and the real result cannot be processed by the Filter chain when it is returned.
In order to solve this problem, PostProcessFilter and AbstractPostProcessFilter were introduced in Dubbo 2.7.0. The PostProcessFilter interface extends from the Filter interface, and AbstractPostProcessFilter is an abstract implementation of PostProcessFilter.
The following is an example of extending the Filter and supporting the asynchronous Filter chain.
Currently, the context we are considering mainly refers to the data stored in the RpcContext. In most scenarios, the user needs to complete the passing of the Context before switching the service thread.
However, AsyncContext also provides the signalContextSwitch() method for a convenient Context switch.