一次线程池有关的性能调优之旅

7 篇文章 0 订阅
5 篇文章 0 订阅

最近在做的项目,在调试时打开了top,发现CPU一直是90%以上的,这让我们感觉奇怪。一般来说写得好的程序,CPU不应该满负载运行。大家决定查找原因。

1、总共有3个大模块,每个模块都是由Quartz定时器触发。所以每次只开放一个,屏蔽掉其他两个。结果发现,本人和大师写的模块单独运行时CPU都很高再见再见

2、刚好那晚是周五,其他人都满怀激动的心情迎接周末,我们一直搞到9点半,排查问题。这个过程也是挺曲折的。首先想了一下程序中哪些地方会耗资源。db连接?db写入?

Socket通讯?线程池?当晚没有找出问题。

3、时间来到了周末,继续在宿舍中探索此问题。我的模块共有3个主要的job,从这里入手。

 <ref bean="loadMessageJobTrigger" />    a
 <ref bean="updataMongoJobTrigger" />   b
 <ref bean="threadPoolJobTrigger" />        c

先只开了a,这是个从MongoDB读取数据到队列的job,一次1w条。运行时cpu稳定在20%,不是这里的问题;

只开b,这是个批量写入MongoDB的job,一次最多也就写1k,也不是这里的问题;

开了a和c,一下子就90%了,看来是c的问题。这是一个用job来启动线程池,定义了一个pool,在while(true)中,判断有空闲thread就添加任务。这里大师在群里说他已经找到是死循环的问题。若非我的也是了。

while (true) {
      if (normalFutures.size() == poolSize) {
                tmpFutureList = new ArrayList<Future<ConsumeMessageThread>>();
                for (int i = 0; i < poolSize; i++) {   // 1
                    future = normalFutures.get(i);
                    if(! future.isDone()) {
                        tmpFutureList.add(future);
                    }
                }
                normalFutures = tmpFutureList;
            }else {  // 2
                ConsumeMessageThread msgThread = (ConsumeMessageThread) BeanHoldFactory
                        .getApplicationContext()
                        .getBean("consumeMessageThread");
                
                map = MessageContainer.waitForPushQueue.poll();
                ......
                msgThread.initMap(map, newSerialId);
                future = (Future<ConsumeMessageThread>) exService.submit(msgThread);
                normalFutures.add(future);            
 }
while(true)中大致如上。如果task总数已经等于线程池中thread数量,就进入1中判断是否有没做完的task,有就放到临时的list中。如果task不满,get一个bean,带上数据,提交到pool中。我先把所有代码都注释,空跑while(true),发现cpu不高。想到可能是task处理中耗资源。我的ConsumeMessagThread中包含了httpClient的代码。便把这几段注掉
client = HttpClients.createDefault();
post = new HttpPost(url);
post.setHeader("Content-Type", "text/html;charset=UTF-8");
ByteArrayEntity e = new ByteArrayEntity(json.getBytes("UTF-8"));
post.setEntity(e);
response = client.execute(post);

再跑,cpu正常。就是这几段的问题了吧。由于这是一个关于消息推送的项目,数据量会很大,必须要快速地把数据推送给三方接口,所以在一个dead loop中不停使用httpclient。client和post要频繁创建并且excute,这是消耗资源的。于是在外层用了一个int变量来记,当循环1000次,就sleep(100)。发现在sleep期间cpu下降了,但是做1000次的期间,cpu照样100%,呈锯齿状波动。看来一次性执行太多httpclient还是不行。把1000改小,波动则小一些。没意思,无论cpu再高,也要成为一条平稳的线才好。那只能dead loop的最后加sleep,不要凑够1000了。这样子就是每个task都会sleep,而不再是任由瞬间的执行,发送速度上也做了妥协。比如说加了sleep(5),理论上的最快速度也只能是200个/s,但是cpu平稳,在i7 4720HQ上平均十几而已。不加sleep,可能是好几百,但是cpu满载。后期这种速度应该是达不到要求了。到时再考虑集群什么的来buff了。集群的话,从数据源做好入口控制,保证不同server发送不同的数据即可。

(注:有个误区就是之前以为空跑while(true)时cpu很低,以为不是死循环的问题。其实是错误的。如果一点代码都不写,几乎不占资源的,但是如果只写一行system.out.print,也是100%)


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值