使用 CompletableFuture 异步组装数据

使用 CompletableFuture 异步组装数据

一种快捷、优雅的异步组装数据方式

实际项目中经常遇到这种情况: 从多个表中查找到数据然后拼装成一个VO返回给前端。
这个过程有可能会非常耗时。因为最终每一条返回的VO数据是由多个表中的数据拼装而成,如果项目还是微服务需要从其他服务获取数据,那将会更加耗时,更加麻烦。简单的几十条、几百条数据单个线程跑起来可能没有什么压力,但是当数量达到成千上万,几十万,几百万,组装的逻辑也变得非常复杂时,这个操作就非常耗时。

最近我在项目中就遇到这个的情况。项目中我们需要做一个相关流程数据的下载功能。
最初版本使用单线程,因为业务的复杂性,5000多条数据完全下载下来需要30min。以为是从数据库分拣数据比较耗时,查询日志后发现数据库查询并没有耗时多久,反而是组装数据占用了大多数时间。

因此机智的我就想起之前同组小伙伴分享的Java8一个新的类CompletableFuture。

CompletableFuture 简介

CompletableFuture 是Java 8 新增加的Api,该类实现,Future和CompletionStage两个接口,提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。

具体大家可以查看Java Api 文档,或者阅读网上一些博客。

CompletableFuture 异步组装数据

代码示例如下

/**
     * 功能描述: 拼装数据
     * @author lkb
     * @date 2019/12/25
     * @param
     * @return java.util.List<com.laidian.erp.crm.vo.DeviceProcessListExportVO>
     */
    private List<DeviceProcessListExportVO> listByFlowJobIds(List<String> flowJobIds, Map<String, ProcessInfoVo> map, Map<Integer,UserInfoDTO> userInfoDTOMap, Map<Integer,HatCity> cityMap){
        //result 列表保存组装完成的数据
        List<DeviceProcessListExportVO> result = new LinkedList<>();
        //每次组装100条数据
        List<List<String>> partition = Lists.partition(flowJobIds,100);
        List<CompletableFuture> futures = partition.stream().map(subList -> CompletableFuture.supplyAsync(() -> {
            //packVOs 方法就是组装数据
            return packVOs(subList,map,userInfoDTOMap,cityMap);
        },ASYNC_IO_POOL).whenCompleteAsync((r,e)->result.addAll(r))
                        .exceptionally(e->{
                            log.error(e.getMessage(),e);
                            log.error("listByFlowJobIds error.");
                            return result;
                        })).collect(Collectors.toList());

        CompletableFuture<Void> all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
        log.info("任务阻塞 ");
        Instant start = Instant.now();
        //阻塞,直到所有任务结束。
        all.join();
        log.info("任务阻塞结束 耗时 = {}",ChronoUnit.MILLIS.between(start, Instant.now()));
        return result;
    }

具体步骤如下:

  1. 将原始数据按照每组100条进行拆分。(具体每组拆分多少条需要根据实际的业务情况和服务器性能,多测试一下应该就知道了)
  2. 多线程组成数据,每个线程组装一组数据(上面拆分的100条原始数据)。packVOs 方法就是组装数据。为了高效,我建议 在组装数据的时候多采用批量,缓存的思想,能批量尽量批量,重复数据就尽量缓存下来。
  3. CompletableFuture.supplyAsync() 方法说明如下。第一个参数是线程需要执行的动作,第二个参数是线程执行用的Executor,可以填自定义的,也可以不填写,不填写程序会使用默认的执行器。

    public static CompletableFuture supplyAsync(Supplier supplier, Executor executor)
    返回由给定执行程序中运行的任务异步完成的新CompletableFuture,其中包含通过调用给定供应商获得的值。

  4. whenCompleteAsync 方法含义和名字一样,将上一步执行的结果或者异常作为参数传给指定的参数。这里我们希望分批组装的结果能过add进result中。
  5. exceptionally 是用来处理异常。当一个线程执行出现异常的时候应该执行怎样的操作。
  6. all.join() 这个方法是等待所有的任务(所有的CompletableFuture)完成。组装数据是耗时的,如果我们不等待所有组装任务完成,直接返回result,相信result中不会有数据,或者数据是不完整的。我们期待的结果是所有的数据都正常组装完成,添加进result。

使用了CompletableFuture方式实现多线程分批组装,并且在组装时采用 “批量+缓存” 的思想,原来5000条数据30min缩短为3min。当然还有优化的空间,但是能达到这个效果已经让我非常满意了。

下次遇到类似的情况,我会优先考虑CompletableFuture分批组装的方式,快捷、优雅。你们有好的方法呢?

原文地址:https://www.cnblogs.com/catlkb/p/12250725.html

时间: 2024-11-02 22:07:57

使用 CompletableFuture 异步组装数据的相关文章

jQuery基础(Ajax,load(),getJSON(),getScript(),post(),ajax(),同步/异步请求数据)

1.使用load()方法异步请求数据 使用load()方法通过Ajax请求加载服务器中的数据,并把返回的数据放置到指定的元素中,它的调用格式为: load(url,[data],[callback]) 参数url为加载服务器地址,可选项data参数为请求时发送的数据,callback参数为数据请求成功后,执行的回调函数. 2.使用getJSON()方法异步加载JSON格式数据 使用getJSON()方法可以通过Ajax异步请求的方式,获取服务器中的数据,并对获取的数据进行解析,显示在页面中,它的

jQuery选取所有复选框被选中的值并用Ajax异步提交数据

昨天和朋友做一个后台管理系统项目的时候涉及到复选框批量操作,如果用submit表单提交挺方便的,但是要实现用jQuery结合Ajax异步提交数据就有点麻烦了,因为我之前做过的项目中基本上没用Ajax来批量提交复选框数据,今天用到了就分享一下. 由于我做的项目一些地方比较复杂,这里我只举一个小例子,能理解就好. 首先,我做了一个简单的多个复选框的界面,如图: 这是一个比较简单的多个复选框提交界面.代码如下: 1 <body> 2 <div> 3 <input type=&quo

串行通讯之.NET SerialPort异步写数据

目 录 第1章 说明    2 1 为什么需要异步写数据?    2 2 异步写数据的代码    2 3 源代码    4 第1章 说明 1 为什么需要异步写数据? 如下图所示,以波特率300打开一个串口. 图1 单击"同步发送"按钮,则数据未发送完之前写数据的函数不会返回.波特率300,每秒大概能发送25个字符,发送500个字符就需要20秒.这20秒之内,整个程序将处于假死状态. 单击"异步发送"按钮,就不会出现假死状态. 2 异步写数据的代码 异步写数据的代码如

android 写行为日志到SD卡 并发处理 异步写入数据到文件不影响界面响应时间

公司在做一个项目 要求记录用户行为,写行为日志文件到SD卡.实现思想 不影响界面用户体验,要时时记录日志 不能漏掉. 1.并发处理日志 写一个类负责管理各个线程传过来的日志数据,日志数据放在队列中等待写线程去处理.这里每次添加一条日志数据都会检查写日志线程是否在工作,同时为了并发处理传过来的数据采用synchronized 同步: ConcurrentLinkedQueue 是基于链接节点的.线程安全的队列.并发访问不需要同步.因为它在队列的尾部添加元素并从头部删除它们,所以只要不需要知道队列的

【转】iphone 输入/输出流异步读写数据

原文:iphone 输入/输出流异步读写数据 分类: iphone2012-05-30 14:50 2484人阅读 评论(1) 收藏 举报 iphoneattributesinterfacepathstreamfile 1.首先是往文件里写入数据 WriteFile.h [cpp] view plaincopy #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @class NoteDb; @interface Wr

Validform和aui2.0结合使用的表单自定义验证提示和列表页异步获取数据Demo

二.添加自定义验证演示       Validform验证提示大多都是在输入框或下拉框的右边或下面.感觉就是不美观,特别是表单字段少的时候,这种在相应对象旁边的提示就没必要了,      它会给人一种不友好的感觉(只是自己的感觉).所以下面写了三种添加演示,希望大家能给出建议,以免下次用起来纠结,不知道用哪种好.        表单验证插件是我们经常使用的Validform_v5.3.2_min.js.先上添加1演示的表单验证js代码,这里我设置了点击提交按钮后才验证.其他添加演示的区别不大,详

使用load()方法异步请求数据

使用load()方法通过Ajax请求加载服务器中的数据,并把返回的数据放置到指定的元素中,它的调用格式为: load(url,[data],[callback]) 参数url为加载服务器地址,可选项data参数为请求时发送的数据,callback参数为数据请求成功后,执行的回调函数. 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/

ASP.NET MVC 第三章 异步提交数据

好长时间都没写文章了本来不准备在写文章了,但是我又为什么今天发表文章咧,第一是为了我自己对技术的认识.第二是为了新手不知道怎么去学习MVC写次系列文章. 废话结束吧,接下来我们看看怎么异步实现提交数据到按钮.本章就是为了WebForm用惯了事件驱动.(此处注明下有很多自认为高手的连事件都不懂是什么,不说懂就算了,直接上MVC吧) 我们根据第二章的修改路由代码接着写,此处因为本人PC不在安装VS2010所以就直接用VS2013了,不在说明怎么创建MVC的示例了.这里我使用MVC4,后面会跟大家一起

ASP模拟POST请求异步提交数据的方法

这篇文章主要介绍了ASP模拟POST请求异步提交数据的方法,本文使用MSXML2.SERVERXMLHTTP.3.0实现POST请求,需要的朋友可以参考下 有时需要获取远程网站的某些信息,而服务器又限制了GET方式,只能通过POST数据提交,这个时候我们可以通过asp来实现模拟提交post数据,网上有挺多这样的例子的.下面的是我自己写的比较简洁易懂的函数. 首先,需要一个编码设置的函数,因为asp一般为gbk的,而标准的网站现在大都使用utf-8的.所以需要转换. 代码如下: function