CompletableFuture使用详解
【Java异常】Variable used in lambda expression should be final or effectively final
CompletableFuture原理与实践-外卖商家端API的异步化
项目接口需要从下游多个接口获取数据,并且下游的网络不稳定还会涉及到循环调用下游接口,导致该接口响应异常慢。
其实调用链路如图所示
cf1 ~ cf5 都需要调用下游接口,导致只能按照顺序,依次调用接口。其实可以将,例如cf1,cf2 拆分成两个异步请求,没必要cf2必须等待cf1请求完成才请求。
之前使用的是同步模型。在同步调用的场景下,接口耗时长、性能差,接口响应时长T > T1+T2+T3+……+Tn。
利用 CompletableFuture 之后,使用的是异步模型,并行从下游获取数据。
Completable实现了两个接口:Future 和 CompletionStage。
随着当前步骤的完成,也可能会触发其他一系列CompletionStage的执行。从而我们可以根据实际业务对这些步骤进行多样化的编排组合,CompletionStage接口正是定义了这样的能力,我们可以通过其提供的thenAppy、thenCompose等函数式编程方法来组合编排这些步骤。
具体使用教程可以参考这边文章CompletableFuture原理与实践-外卖商家端API的异步化,描述的十分详细和生动。
cf1,cf2由于没有任何依赖,可是需要返回值,故设计为
CompletableFuture<List<xxxDto>> cf1 = CompletableFuture.supplyAsync(() -> {
//业务处理
if (CollectionUtils.isEmpty(resp.getData())) {
//其他线程抛出异常
}
return resp.getData();
}).exceptionally(ex -> {
//主线程处理其他线程抛出的异常
});
cf3,cf5由于依赖 cf1, cf2 ,所以可以通过thenApply、thenAccept、thenCompose等方法来实现
但注意,返回数据类型必须相同。
CompletableFuture<xxxDto> cf3 = cf1.thenApply(result1 -> {
//result1为cf1的结果
//......
return resp.getData();
});
CompletableFuture<xxxDto> cf5 = cf2.thenApply(result2 -> {
//result2为cf2的结果
//......
return resp.getData();
});
cf4,依赖cf1,cf2 ,这种二元依赖可以通过thenCombine等回调来实现
CompletableFuture<String> cf4 = cf1.thenCombine(cf2, (result1, result2) -> {
//result1和result2分别为cf1和cf2的结果
return "result4";
});
由于cf6依赖cf3,cf4,cf5,这种多元依赖可以通过allOf
或anyOf
方法来实现,区别是当需要多个依赖全部完成时使用allOf
,当多个依赖中的任意一个完成即可时使用anyOf
CompletableFuture<Void> cf6 = CompletableFuture.allOf(cf3, cf4, cf5);
CompletableFuture<String> result = cf6.thenApply(v -> {
//这里的join并不会阻塞,因为传给thenApply的函数是在CF3、CF4、CF5全部完成时,才会执行 。
result3 = cf3.join();
result4 = cf4.join();
result5 = cf5.join();
//根据result3、result4、result5组装最终result;
return "result";
});
需注意,如果cfx里用到方法的局部变量需要设置为final,避免completablefuture在使用该变量的时候被修改。
按照类似 “观察者模式” 的设计思想
接口响应从 TP99=3s左右,提升到2s左右