Backend Development 19 min read

Implementing Dubbo Asynchronous Calls with CompletableFuture: Practices and Performance Gains

By refactoring Dubbo RPC interfaces to return CompletableFuture, applying thenApply/thenCombine/thenCompose patterns, isolating a custom business thread pool, and handling errors and tracing, the team achieved up to 50% latency reduction, 25% response‑time improvement, a one‑third server cut and CPU utilization rise, demonstrating substantial performance and cost benefits.

DeWu Technology
DeWu Technology
DeWu Technology
Implementing Dubbo Asynchronous Calls with CompletableFuture: Practices and Performance Gains

Background

Since Dubbo 2.7.0, all asynchronous programming interfaces are based on CompletableFuture . Asynchronous Dubbo can greatly improve interface performance and reduce blocking between dependent calls. In a cost‑reduction project, the customer‑service robot team applied Dubbo async to several high‑QPS services and observed a 50% performance boost and a one‑third reduction in application servers.

Dubbo Async Implementation

By moving complex business logic from the default Dubbo thread pool (size 200) to a custom business thread pool, the request handling capacity of the Dubbo pool is increased while resource utilization improves.

2.1 Interface Refactor

The original synchronous method Result<RecommendAtConnectRes> getRecommendContent(RecommendAtConnectReq request) is kept for compatibility, and a new asynchronous method returning CompletableFuture<Result<RecommendAtConnectRes>> is added.

public interface RecommendAtConnectApi {
    Result<RecommendAtConnectRes> getRecommendContent(RecommendAtConnectReq request);
    CompletableFuture<Result<RecommendAtConnectRes>> asyncGetRecommendContent(RecommendAtConnectReq request);
}

2.2 Future Usage Patterns

Common patterns include:

Result transformation with thenApply

Combining multiple futures with thenCombine (or allOf for more than two)

Sequential dependency with thenCompose

Example of thenApply :

CompletableFuture<String> cFuture = cAsyncService.asyncSayHello(name);
CompletableFuture<DataDTO> finalFuture = cFuture.thenApply(c -> new DataDTO());
return finalFuture;

Example of thenCombine :

CompletableFuture<String> cFuture = cAsyncService.asyncSayHello(name);
CompletableFuture<String> dFuture = dAsyncService.asyncSayHello(name);
CompletableFuture<DataDTO> allFuture = cFuture.thenCombine(dFuture, (c, d) -> new DataDTO());
return allFuture;

Example of thenCompose (with optional handling):

CompletableFuture<Optional<RecommendAtConnectDto>> taskEngineFuture = pushGsTaskEngineHandler.asyncPushHandler(connectRequest);
CompletableFuture<Optional<RecommendAtConnectDto>> refundFuture = getNextFuture(taskEngineFuture, connectRequest, unused -> pushLogisticsRefundHandler.asyncPushHandler(connectRequest));
return refundFuture;

2.3 CompletableFuture Internals

The core fields are result (the computed value) and stack (a Treiber stack of dependent actions). Methods such as thenApply , thenCombine , and thenCompose create specific Completion objects, push them onto the stack via CAS, and fire them when the result becomes available.

Key source snippets:

// thenApply implementation
private
CompletableFuture
uniApplyStage(Executor e, Function
f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture
d = new CompletableFuture<>();
    if (e != null || !d.uniApply(this, f, null)) {
        UniApply
c = new UniApply<>(e, d, this, f);
        push(c);
        c.tryFire(SYNC);
    }
    return d;
}

3.1 Practical Experience – Scenario Selection

Three robot scenarios were chosen: order detail page, chat “you may ask” page, and input suggestion. They have high QPS, are IO‑intensive, and do not affect core dialogue interfaces, making them ideal for async conversion.

3.2 Best Practices

3.2.1 Dependency Mapping

Before refactoring, draw a dependency graph to identify parallel and serial relationships. Parallel CF nodes can be executed concurrently, while dependent nodes must be chained.

3.2.2 Code Example

public CompletableFuture
getResult(){
    // Parallel three chains
    CompletableFuture
cf1 = cf1Service.getResult();
    CompletableFuture
cf2Combine = getCf2Combine();
    CompletableFuture
cf3Combine = getCf3Combine();
    // Combine and transform
    CompletableFuture
finalFuture = CompletableFuture.allOf(cf1, cf2Combine, cf3Combine);
    return finalFuture.thenApply((unused) ->
        new CFResponse(cf1.get().getCf1Value() + cf2Combine.get().getCf2CombineValue() + cf3Combine.get().getCf3CombineValue()));
}

private CompletableFuture
getCf2Combine() {
    CompletableFuture
cf2 = cf2Service.getResult();
    return cf2.thenCompose(cf2Response -> {
        CompletableFuture
cf4 = cf4Service.getResult(cf2Response.getCf2Value());
        return cf4.thenApply(cf4Response -> new CF2CombineResponse(cf4Response.getCf4Value()));
    });
}

private CompletableFuture
getCf3Combine() {
    CompletableFuture
cf3 = cf3Service.getResult();
    return cf3.thenCompose(cf3Response -> {
        CompletableFuture
cf5 = cf5Service.getResult(cf3Response.getCf3Value());
        CompletableFuture
cf6 = cf6Service.getResult(cf3Response.getCf3Value());
        return CompletableFuture.allOf(cf5, cf6)
            .thenCompose(unused -> cf7Service.getResult(cf5.get().getCf5Value(), cf6.get().getCf6Value()));
    });
}

3.2.3 Thread‑Pool Isolation

Define a custom business thread pool to avoid blocking Dubbo’s internal pool. Example Spring bean:

@Bean(name = "dubboAsyncBizExecutor")
public ThreadPoolTaskExecutor dubboAsyncBizExecutor(){
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(200);
    executor.setMaxPoolSize(200);
    executor.setQueueCapacity(50);
    executor.setThreadNamePrefix("dubboAsyncBizExecutor-");
    executor.setRejectedExecutionHandler((r, e) -> log.error("dubbo async biz task exceed limit"));
    return executor;
}

Use it with CompletableFuture.supplyAsync(..., dubboAsyncBizExecutor) .

3.2.4 Exception Handling

Handle errors with exceptionally and unwrap CompletionException via a utility method.

CompletableFuture
asyncPushContent(RecommendAtConnectRequest request) {
    CompletableFuture
future = orderSourcePredictHandlerChain.asyncHandleOfPredict(request);
    return future.thenApply(body -> {
        if (StrUtil.isBlank(body)) return null;
        return RecommendAtConnectDtoUtil.getDto(body, ...);
    }).exceptionally(err -> {
        log.error("async error", ExceptionUtils.extractRealException(err));
        return null;
    });
}

public class ExceptionUtils {
    public static Throwable extractRealException(Throwable t){
        if (t instanceof CompletionException || t instanceof ExecutionException) {
            if (t.getCause() != null) return t.getCause();
        }
        return t;
    }
}

3.2.5 Stability Guarantees

Keep original synchronous methods unchanged.

Introduce async methods alongside them.

Control traffic via AB testing to switch between sync and async.

Provide a one‑click rollback to the original logic.

3.3 Issues Encountered

Trace‑ID loss in async callbacks, requiring monitoring support.

Thread‑pool isolation only available from Dubbo 3.2.0.

thenCompose cannot return null ; wrap with Optional .

Logging must be placed inside callbacks to capture real results.

Monitoring platforms may not include callback latency, making performance diagnosis harder.

Asynchronous Benefits

Load test shows 50% interface latency reduction.

Production RT decreased by ~25% (e.g., input‑suggestion from 173 ms to 119 ms).

Server count reduced by one‑third, CPU utilization increased from ~18% to ~50%.

Conclusion

The practice demonstrates that converting Dubbo RPC to asynchronous CompletableFuture calls removes blocking between services, expands thread‑pool capacity, and improves CPU utilization, enabling higher throughput with fewer hardware resources. This contributes to cost‑reduction and efficiency gains for the organization.

JavaPerformanceDubbothreadpoolasynchronousCompletableFuture
DeWu Technology
Written by

DeWu Technology

A platform for sharing and discussing tech knowledge, guiding you toward the cloud of technology.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.