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

来源:互联网 发布:怎么给淘宝刷好评赚钱 编辑:程序博客网 时间:2024/05/18 03:56

最近在做的项目,在调试时打开了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 0