一、需求背景
相信大家入职到一个新公司(xinkeng),一般都会被分配去解决千古难题(caipigu),这些问题大概率很多不想去解决,并且代码很多复杂,各种套娃。领导为检验新人的能力,就会去让新人去解决这种问题,一方面可以了解业务代码,还能顺手把没人愿意做的事做了。但是,你做不好也没关系,大佬也会说,这个其实都是历史问题了,大家是不是有遇到这样奇葩新手工作呢?欢迎大家评论区讨论。今天给大家分享一个比较常见的问题,接口超时或者响应时间比较长怎么解决。
本文主要介绍,如何同个CompletableFuture异步编排,来解决分布多次查询,导致接口响应慢的问题。
二、CompletableFuture介绍与实战
1.为什么要用CompletableFuture呢?
大家都知道,多线程可以解决,多次查询第三方接口或者数据库耗时的问题?但是有种情况,可能就很难去解决了,比如一个异步调用可能会依赖另一个异步调用的执行结果。因为多线程一整个是一个任务,多线程可能就不好处理。所以Java8就推出了一个新的并发框架—CompletableFuture。可以参考书籍:《实战java高并发程序设计》 、《Java8实战》
2.CompletableFuture常用接口方法
1)CompletableFuture启动异步任务
CompletableFuture提供四种静态方法来创建一个异步操作,可以自定义线程池,否没有传Executor则会使用默认线程池。
无返回值的:
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
有返回值的:
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
2)CompletableFuture完成回调以及异常感知
监听正常和异常结果
whenCompleteAsync((res,excption):第一个参数是返回结果,第二参数是异常
whenComplete: 是执行当前任务的线程用来继续执行whenComplete的任务
whenCompleteAsync: 是执行whenCompleteAsync的任务继续提交个线程池来进行执行
处理异常结果
exceptionally
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 10 / 0;
System.out.println("运行结果:" + i);
return i;
}, threadPool)
.whenComplete((res,excption)->{
System.out.println("结果是:"+res + ",异常是:"+excption);
}).exceptionally(throwable ->{
// ruguo 异常就会返回改结果
return 10;
});
3)CompletableFuture最终处理结果
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 10 / 0;
System.out.println("运行结果:" + i);
return i;
}, threadPool)
.handle((res,excption)->{
if(res != null){
return res * 2;
}
if(excption != null){
return 0;
}
return 0;
});
4)CompletableFuture线程串行化
thenRunAsync:没有返回值值,不能接受上一步的结果
thenAcceptAsync:没有返回值值,能接受上一步的结果
thenApplyAsync:有返回值值,能接受上一步的结果
/*
thenRunAsync:没有返回值值,不能接受上一步的结果
* */
CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 10 / 5;
System.out.println("运行结果:" + i);
return i;
}, threadPool).thenRunAsync(()->{
System.out.println("任务2启动。。。");
},threadPool);
/*
thenAcceptAsync:没有返回值值,能接受上一步的结果
* */
CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 10 / 5;
System.out.println("运行结果:" + i);
return i;
}, threadPool).thenAcceptAsync(res->{
System.out.println("任务2启动。。。"+res);
},threadPool);
/*
thenApplyAsync:有返回值值,能接受上一步的结果
* */
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getName());
int i = 10 / 5;
System.out.println("运行结果:" + i);
return i;
}, threadPool).thenApplyAsync(res->{
System.out.println("任务2启动。。。"+res);
return res +1;
});
5)CompletableFuture多任务任务组合
CompletableFuture.allOf:主线程所有任务完成执行,用于解决多个任务单独get阻塞问题
CompletableFuture.anyOf:主线程任意1个任务完成执行
CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品的图片信息");
return "hello.img";
}, threadPool);
CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品的属性");
return "黑色+256G";
}, threadPool);
CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("查询商品介绍");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "华为";
}, threadPool);
// 顺序获取,同步执行,效率低
/* futureImg.get();
futureAttr.get();
futureDesc.get();*/
CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureAttr);
allOf.get();
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureAttr);
anyOf.get();
3.CompletableFuture工作实战案例
这是本人在电商公司中,解决一个问题,对于电商一个商品(sku)包含的信息需要查找很多表或者接口,如果全都用同步来处理肯定是比较慢的,如果用多线程,对于依赖调用又不好处理。像sku基本信息、sku的图片信息互不依赖可以异步执行,但是获取商品的销售属性组合、获取商品的介绍、获取商品的规格参数信息都要依赖sku基本信息执行结果,才能进一步去查询。所以,显然CompletableFuture首选。不说废话,直接上关键代码案例:
//1.sku基本信息获取
CompletableFuture<SkuInfoEntity> skuInfoFuture = CompletableFuture.supplyAsync(() -> {
SkuInfoEntity info = getById(skuId);
skuItemVo.setInfo(info);
System.out.println("skuInfoFuture...1");
return info;
}, threadExecutor);
//2.获取spu的销售属性组合
CompletableFuture<Void> saleAttrFuture = skuInfoFuture.thenAcceptAsync((res) -> {
List<SkuItemSaleAttrVo> saleAttr = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
skuItemVo.setSaleAttr(saleAttr);
System.out.println("saleAttrFuture...2");
}, threadExecutor);
//3.获取spu的介绍
CompletableFuture<Void> descFuture = skuInfoFuture.thenAcceptAsync((res) -> {
SpuInfoDescEntity spuInfoDesc = spuInfoDescService.getById(res.getSpuId());
skuItemVo.setDesc(spuInfoDesc);
System.out.println("descFuture...3");
}, threadExecutor);
//4.获取spu的规格参数信息
CompletableFuture<Void> attrGroupFuture = skuInfoFuture.thenAcceptAsync((res) -> {
List<SpuItemAttrGroupVo> groupAttrs = attrGroupService.getAttrGroupBySpuId(res.getSpuId(), res.getCatalogId());
skuItemVo.setGroupAttrs(groupAttrs);
System.out.println("attrGroupFuture...4");
}, threadExecutor);
//5.sku的图片信
CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
List<SkuImagesEntity> images = imagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);
System.out.println("imageFuture...5");
}, threadExecutor);
//6.查询当前sku是否参与秒杀优惠
CompletableFuture<Void> seckillSkuInfoFuture = CompletableFuture.runAsync(() -> {
Result<SeckillSkuRedisVO> seckillSkuInfo = seckillFeignService.getSeckillSkuInfo(skuId);
if (seckillSkuInfo.getCode() == 200) {
SeckillSkuRedisVO skuInfoData = seckillSkuInfo.getData();
skuItemVo.setSeckillSkuVo(skuInfoData);
}
}, threadExecutor);
CompletableFuture.allOf(skuInfoFuture, saleAttrFuture, descFuture, attrGroupFuture, imageFuture, seckillSkuInfoFuture).get();
三、CompletableFuture总结
CompletableFuture真的很适合做异步任务,或者接口多次调用第三服务等复杂操作,可以极大的提高程序响应速度。重要的是,可以提升java开发能力,对于日常工作,在整天写curd的过程中,反复思考有没有更好的方案,比如多线程,异步编排等。而不是老是写流水线代码。
评论区