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