批量处理请求内容,Java使用多线程处理业务,仍然很慢,CPU使用率很低,问题在哪呢?
问题描述
批量处理请求内容。程序接收一个请求,请求中的list中有1000个Map,要用这1000个Map的数据去调用同一个方法进行处理,处理完成后,把这1000个处理结果响应回去。
问题出现的环境背景及自己尝试过哪些方法
调用的处理业务的方法执行时间为1~2秒。
业务处理内容:
{
1.字段校验2.查询数据库,根据返回结果进行校验(耗时)
3.查询数据库,根据返回结果进行校验(耗时)
4.将请求数据部分字段处理然后保存到数据库
5.使用第4步中处理完的数据调用其他系统等待结果(耗时,300~800毫秒左右)
6.根据第5步返回的结果更新第4步中存入数据库的信息(耗时)
7.返回处理结果
}
尝试方案1:使用Java8的parallelStream()进行并行处理。
结果:不指定线程数时,最多使用4个线程。指定线程数后,最多使用16个线程。且CPU使用率都很低
尝试方案2:使用线程池(定长线程池和缓冲线程池)
结果:执行需要时间仍然很长,CPU使用率很低。
相关代码
//如下为部分代码
方案1:
List<Map<String,Object>> reqMapList = reqMap.get("reqList");List<Map> resultList = new ArrayList<>();
reqMapList.parallelStream().forEach(map -> {
Map<String, Object> map1 = doService.doService(map);
resultList.add(map1);
}))
List<Map<String,Object>> reqMapList = reqMap.get("reqList");List<Map> resultList = new ArrayList<>();
new ForkJoinPool(parallelism).submit(() -> reqMapList.parallelStream().forEach(map -> {
Map<String, Object> map1 = doService.doService(map);
resultList.add(map1);
})).get();
//方案2
ExecutorService tpe = Executors.newCachedThreadPool();ExecutorService tpe = Executors.newFixedThreadPool(50);
//存储线程的返回值
List<Future<Map<String, Object> >> results = new LinkedList<Future<Map<String, Object> >>();
for (Map<String, Object> map : reqMapList) {
//执行任务的线程
Task task = new Task(map);
//调用submit可以获得线程的返回值
Future<Map<String, Object>> result = tpe.submit(task);
results.add(result);
}
你期待的结果是什么?实际看到的错误信息又是什么?
想在30秒内处理完这1000笔业务。请各位给点想法建议,不胜感激~
回答:
看了下伪代码流程,感觉可以优化下流程,其实大部分应该在数据库的IO开销上(可能数据库量比较大),以及外部RPC的接口的消耗上。其实第4步数据存储和第6步再更新的逻辑,没必要第四部就存储,量不大的情况可以放内存,直接数据处理完了第6步再统一存储。以及RPC的调用,可以分批并发量小的数据去请求
回答:
根据实际使用场景,通过数据进行分配线程,给每个线程处理的数据分配的比如一个线程执行100条数据,则创建10线程,,分情况吧 在有的时候通过自己计算分配还要好一点
然后就是数据库这种能批量尽量批量。
回答:
感觉你这个问题瓶颈出在第5步的其他系统上。整个流程基本都是io的耗时操作,cpu操作很少上不去也正常,io操作多考虑增加线程是没问题的,但是使用newCachedPool基本就是把线程数开到最大了,剩下就是io的影响了,数据库问题应该不大,那最可能出问题的就是第三方系统了。况且你这线程越多,计算量又不大,就代表你给它的并发请求越多,出现性能问题也比较正常。建议压测一下第三方系统。在这之前可以把第5步去掉,测试一下本系统自身的性能
回答:
parallelStream的默认线程数量是你cpu逻辑核心数量,但是可以调整的,不可能最高16。
然后你这种计算量小,线程大部分时间都堵塞在等待网络响应的,cpu就会占用少,推荐开一个线程池,多用一些线程,比如128个线程。只要你数据库或其他服务扛得住。
具体多少线程最好,假设一次请求如果按照2秒算,一个线程可以30秒内发送15次请求。30秒内完成的花,需要1000/15 = 67个线程。
还有从代码逻辑上修改,就是你2和3步骤,是否能改成批量查询,比如把1000条的一次性查出来。然后第4步和第5步可以并行,比如把第4步放入线程池,然后直接执行第5步,通过同步机制,保证执行第6步前第4步执行完。
回答:
为什么CPU占用低速度却上不去? 本质上你机器做的事情就是把参数写入网络然后等待数据库或第三方返回结果. 这个流程中你机器做的最多的事情就是等待. CPU占用怎么会高.
想要速度快, 最理想的状况是使用异步IO, 一个线程处理这些事情就OK了,而不是纠结需要到底要开几个线程.
第三方接口的网络异步有很多解决方案,数据库这块java的异步IO真的不少找(JDBC本质就是一个阻塞调用,好多宣称自己支持异步数据库的库只要它依赖JDBC,肯定就是假的.真正异步的数据库要从网络层裸写数据库调用,不可以引入类似mysql-connector的官方jdbc实现,那样肯定还是阻塞的.)
实在找不到数据库解决方案的话,你可以用一个自己熟悉的带异步数据库接口的语言专门处理这类事情,nodejs或者go都是不错的选择.再不然你直接开1000个线程得了.
回答:
数据库操作是io阻塞的。
以上是 批量处理请求内容,Java使用多线程处理业务,仍然很慢,CPU使用率很低,问题在哪呢? 的全部内容, 来源链接: utcz.com/p/944192.html