CompletableFuture(redis中key的过期时间设置的是一个小时)
-
通过内部的性能监控工具进行测试发现,优化前的请求耗时为300毫秒左右,优化后降为了100毫秒。吞吐量tps是2000。
-
CompletableFuture 是 Java8 新增的一个支持非阻塞的类,用于异步编程来编写非阻塞的代码,使任务运行在一个单独的线程中,与主线程隔离,在这种方式中,主线程不会被阻塞,不需要一直等待子线程完成,从而使主线程可以并行的执行其他任务,让我们把耗时的操作从我们本身的调用线程中释放了出来,就好像我们去饭店吃饭,不需要我们自己去煮饭,我们点餐后就可以继续干自己的事,然后餐厅煮好了就回调我开始吃饭。
-
CompletableFuture 实现了 Future 和 CompletionStage接口,并且提供了许多关于创建,链式调用和组合多个 Future 的便利方法集,而且有比较好的异常处理支持。
-
CompletableFuture.supplyAsync() (舍pe耐 额think)。它内部有一个 asyncSupplyStage 方法(它是并行执行的),如果没有给它传入 executor(以可摘cute)执行者,它就会使用 ForkJoinPool.commonPool() 作为它的线程池 执行异步代码。
-
CompletableFuture用的是默认的线程池,在处理过程中我询问了负责人,他们给的答案是这里采用默认的即可,因为两者都可以实现目的。之所以没有复用项目中已有的线程池,是因为我们的线程池根据业务进行了隔离,不同的业务公用一个线程池的话,如果某个业务执行的很慢,就会影响其他业务了,所以不同的业务拥有自己独立的线程池。
-
runAsync方法不支持返回值。supplyAsync可以支持返回值。
-
传入参数是协议参数和skuId的set集合,通过四个不同的查询接口去主站中查找对应数据,查到后进行组合,得到的是一个key为skuId,value为sku详情的map。组合过程是通过skuId来从四个数据中获取sku详情所需的字段,中间会经历一些业务所需格式的转换和组合。
-
如果异步过程中需要用到前面的返回结果,可以通过 CompletableFuture 的 thenCompose 方法来实现,它可以对两个 CompletionStage 进行流水线操作,第一个操作完成时,将其结果作为参数传递给第二个操作:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> f = CompletableFuture.supplyAsync(() -> {
int t = 3;
System.out.println("t1=" + t);
return t;
}).thenCompose(param -> CompletableFuture.supplyAsync(() -> {
int t = param * 2;
System.out.println("t2=" + t);
return t;
}));
System.out.println("thenCompose result : " + f.get());
}
}
返回结果是 t1=3,t2=6,thenCompose result :6
说辞
- 在从京东的主数据商品池查找商品的基本信息、大字段信息、图片信息和扩展信息时,我最开始使用的是简单的查询逻辑进行的查找,整体是一个按顺序串行执行的过程。
- 很显然,查询完这四种数据所花费的时间是分别查询他们的时间总和。于是我改进了这个方案,整体改进逻辑是将串行的执行的过程优化为并行的执行过程。
- 实现的方法是,采用了Java8的CompletableFuture方法,它提供了一个异步运算结果的建模,让我们把耗时的操作从本身的调用线程中释放出来,只需要完成后进行回调即可,就好像我们去饭店吃饭,不需要我们自己去煮饭,我们点餐后就可以继续干自己的事,然后餐厅煮好了就回调我开始吃饭。
- 通过这个改进,查询完这四种数据所花费的时间就是这四个查询中耗时最长的那个。相比优化前,降低了整体耗时。通过内部的性能监控工具进行测试发现,优化前的请求耗时为300毫秒左右,优化后降为了100毫秒。吞吐量tps是2000。
- 我通过编写测试用例进行了测试。测试代码的编写逻辑大致是创建一个线程,调用优化前的这个查询接口,输出查询所耗时长。
/**
* 查询渠道商品(包含基本信息、图片、大字段、扩展信息、装吧) 目前提供给jos
*/
public Map<Long, SkuDetailDto> queryProductDetail(CrsDto crsDto, Integer pureSkuMark) {
Protocol protocol = getProtocol();
CompletableFuture<Map<Long, SkuBaseInfoDto>> skuBaseInfosFuture =
CompletableFuture.supplyAsync(() -> productRpcWrap.querySku(protocol, crsDto));
CompletableFuture<Map<Long, SkuBigFieldInfoDto>> skuBigFieldsFuture =
CompletableFuture.supplyAsync(() -> assemblyRpcWrap.queryProductBigFieldBySku(protocol, crsDto));
CompletableFuture<Map<Long, List<ImageDto>>> skuImagesFuture =
CompletableFuture.supplyAsync(() -> materialRpcWrap.queryImage(protocol, crsDto));
CompletableFuture<Map<String, AttributeSettingResult>> skuSpecificationExtAttrsFuture =
CompletableFuture.supplyAsync(() -> materialRpcWrap.queryNewAttributeSettingForAssembly(protocol, crsDto));
Map<Long, SkuBaseInfoDto> skuBaseInfos = null;
Map<Long, SkuBigFieldInfoDto> skuBigFields = null;
Map<Long, List<ImageDto>> skuImages = null;
Map<String, AttributeSettingResult> skuSpecificationExtAttrs = null;
try {
skuBaseInfos = skuBaseInfosFuture.get();
skuBigFields = skuBigFieldsFuture.get();
skuImages = skuImagesFuture.get();
skuSpecificationExtAttrs = skuSpecificationExtAttrsFuture.get();
} catch (InterruptedException | ExecutionException e) {
log.error("查询渠道商品信息异常,skuIds={}", crsDto.getSkuIds(), e);
throw new RuntimeException("查询渠道商品信息异常!");
}
return constructSkuBaseInfo(skuBaseInfos, skuBigFields, skuImages, skuSpecificationExtAttrs,pureSkuMark);
EasyExcel
- 可以通过 @ExcelProperty(“客户Id”) 注解来指定每个字段的列名称,以及下标位置。
@RequestMapping("/exportData")
@ResponseBody
public Result export(ProductInfoExtVo productInfoExtVo) {
QueryProductExt queryProductExt = getQueryProductExt(productInfoExtVo);
String fileName = "/export/Data/" + System.currentTimeMillis() + ".xlsx";
File file = new File(fileName);
ExcelWriter excelWriter = null;
try {
excelWriter = EasyExcel.write(fileName, ProductExtDetailShow.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("ProductExtDetail").build();
int i = 1;
PageInfo<ProductExt> page;
do {
page = PageUtils.page(i, 1000, () -> productExtMapper.getProductInfoExt(queryProductExt));
List<ProductExtDetail> productExtDetail = getProductExtDetails(page.getList());
List<ProductExtDetailShow> detailShow = getProductExtDetailShow(productExtDetail);
excelWriter.write(detailShow, writeSheet);
i++;
} while (i <= page.getPages());
} finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
Boolean aBoolean = ctpJssHelper.uploadFile2WareJss(file.getName(), file);
if (!aBoolean) {
throw new CtpException("上传文件到jss失败");
}
file.delete();
return Result.createWithSuc(ctpJssHelper.getWareFileUrl(file.getName()).toString());
}