JavaCpu跑高思路分享(如何充分使用CPU资源)

JavaCpu跑高思路分享(如何充分使用CPU资源)

背景

最近在做一个离线数据加工的项目,需要在本地应用中进行一些计算和加工。在吭哧吭哧写完第一版本后,发现应用的吞吐并上不去且CPU的使用率特别的低。在网上搜索后发现,大部分人遇到的都是Java应用CPU使用率太高怎么降下来。所以在完成一定的优化后,写下这篇文章记录下来,并希望能给大家提供一个分析的思路。

场景

请添加图片描述

一张图简单交代一下这个应用。系统分为三块:数据获取、数据加工和数据储存。连通两两之间的是Queue(linkedBlockQueue)。

在进行优化前需要了解每一个模块主要使用到的资源:

  1. 数据获取模块:因为涉及到外部数据源获取,网络IO为主要的瓶颈,只需要少部分CPU时间做反序列化操作。
  2. 数据加工模块:不涉及外部数据源获取,主要是CPU的一些计算工作,消耗CPU资源。
  3. 数据储存模块:主要是把数据已本地文件的方式进行存储,消耗磁盘IO资源。

优化思路

  1. 在分析完每个模块之后,我们再来思考一下整件事情。我们关心的本质其实是吞吐比较低,而CPU使用率比较低只是一个比较好观察到的机器指标,从而协助我们往这个方向思考。看回前面的模块上面的分析,CPU使用率较低可以推断出来加工模块并不繁忙(可以通过jstack和arthas定位)。
  2. 在定位到是加工模块不繁忙后,就可以考虑是供给侧不足还是输出侧拥堵导致无法消费。由于数据储存模块为单线程写本地磁盘,通过定位DiskIO和观察写本地文件线程状态可以得知线程非常空闲,写本地磁盘并不是瓶颈。
  3. 那么开始定位数据供给侧不足的问题,这里可能会有两种情况,1. 队列中有足够的数据,但由于Queue的线程安全保护导致性能不足(CAS或者读写锁)2. 队列中没有足够的数据,导致线程CPU时间被释放。我在定位这个问题的时候,专门针对poll/take方法记录了poll和take时候的size为0的次数。代码如下:
	@Override
    public E take() throws InterruptedException {
        if(isEmpty()){ //继承 LinkedBlockingQueue, isEmpty为常量时间
            bizMetricsService.countRequestMetrics(monitorTouchZeroName, 1);
        }
        return super.take();
    }
  1. 通过上面的监控能够定位出来是take()是队列为空的情况非常多,继续向数据获取模块定位。由于数据获取模块主要依赖网络IO读取。所以第一思路是去看网络IO的负载。可在我这次优化中,网卡的负载仅为2%,并且通过单独运行数据获取模块(即不往队列里写数据)能够确认网络IO并不是影响系统吞吐的原因。
  2. 分析完运行环境的原因后开始分析代码,系统中有一个这样的逻辑,当消息获取模块发现队列长度达到一定阈值后会进行一个sleep。在原计划的开发里面这个队列满了以后就需要暂停获取让后续的数据处理把队列数据消耗完成。
Queue waitProcessQueue = new LinkedBlockingQueue(500);
if(waitProcessQueue.size()>Threshold){
	//在第一阶段定位中,这个日志一直在print.所以会认为数据加工能力不足	
	//但慢慢摸寻,发现问题并不是处理能力不足的问题
	printlog("wait comsume....")	
	doSleep(sometime);
}
  1. 在完成4/5两个阶段的定位后,问题就已经清晰了。下游的数据加工模块在数据获取模块sleep的时间里已经很快的把待处理的数据消耗完成,并开始空take的等待。
  2. 那么优化的话,就是拉长队列的长度(这里需要注意内存的消耗),并调整sleep的时间为下游消费者(数据加工模块)不可完全消耗在sleep阶段完全消耗掉所有待处理数据的时间。

总结&补充

  1. sleep本来计划用于让后面加工模块消耗待处理数据反而成为性能的瓶颈。而在使用了LinedBlockingQueue的情况下其实完全可以等到队列满的时候被写锁锁住再进行sleep。
  2. 如果最早的时候增加数据处理单条数据的平均时间,也比较好推断出来是否待数据供给不足。
  3. 在完成开发的时候数据加工模块的线程池数量被我随意设置成超过CPU核数数倍。这里就会存在线程切换的问题,从而导致CPU无效使用。在后续优化的过程中,被设置为3/4的CPU核数(根据系统/业务逻辑测试吞吐量得出,这个一般就是经验值了)。

给自己的思考

  1. Java线程切换问题其实是个未经验证的假设。这里缺少了监控/指标上的证明,同时也缺少了如何监控/确认Java线程切换的能力
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值