生产项目中queue同步问题导致项目部署后CPU爆表问题解决
2016-06-08 10:53
621 查看
1.问题描述
最近参与到一个新项目的开发当中,项目的大部分功能已经由另外一名同事实现,这名同事给我反映了一个问题,说每次部署这个项目成功之后CPU立马飙到将近百分之百,一直没发现问题在哪里。
2.解决过程
听了同事的描述之后,心想肯定是程序性能问题,由于之前一直没碰到过这类问题,怀着浓厚的好奇心想一探究竟,解决这类问题本省也是成长,说干就干。
我首先想到的解决这类问题是使用性能分析工具——Java VisualVM,JDK自带的可视化工具。
通过VisualVM可以看到,除了CPU爆表之外其他都比较正常。
再看看线程运行情况,按运行时间排序,发现占用CPU最多的线程如绿色部分,其中有RMI相关线程、I/O线程。
看到这个排名,首先怀疑是RMI远程调用相关问题,由于项目中使用了Dubbo等框架,再加上性能分析经验不足,一直以为问题的产生跟dubbo相关,然后就去项目中去掉dubbo相关的功能(虽然有点二,当时觉得值得试一试),花了不少功夫终于去掉dubbo可以重新部署了。
重新部署项目后,发现CPU突然又飙升上去了。显然与dubbo关系不大,接下来的怀疑对象就是OpenapiMonitorAsyncWriteLogThread这个线程,由于并不是我写的代码,所以并不了解这个线程的作用。我们接着生成线程的Dump文件:
接着看Dump文件,找到OpenapiMonitorAsyncWriteLogThread这个线程的相关信息,我们就可以跟踪到相应的代码了
到这里我们只是怀疑OpenapiMonitorAsyncWriteLogThread这个线程有问题,并没有确定,那么我们再看看抽样器对CPU抽样(如下图),发现这个线程中的pollOne()方法占用CPU很多,所以我们更加怀疑这个线程,所以还是值得去仔细研究一下这个线程中的代码。
看了代码之后发现,这个线程实现的功能就是在项目部署后创建一个线程,这个线程不断的向数据库中保存访问请求日志,这些日志对象首先放到一个队列当中,然后线程不停的从queue当中取出对象:
先前我们怀疑的pollOne()方法很简单,就是一个调用queue.poll()(这个queue是用的LinkedBlockingQueue)。我们去看看这个poll()方法的源码有什么特别之处呢:
经过仔细琢磨,终于发现问题。
这个循环会一直调用pollOne()这个方法,也就是上图中的poll()方法,然而当queue为空的时候,那么poll()方法就会直接返回,继续回到while循环当中,如果while循环能够终止那么也不会有问题,关键是while循环所在方法一直在运行,如下图:
从而While循环所在方法会一直运行,所以问题就出在这里,到此为止我们已经找到了问题所在,就是从queue中获取日志对象的方法不管queue是否为空都会一直运行,就导致了CPU被这个线程一直消耗。
3.问题解决
我们上面已经知道了问题之所在,现在要做的就是解决问题。既然poll()方法在队列为空的情况下还会一直继续运行,那么我们就不应该用此方法来获取队列中的对象,而是使用take()方法。
显然take()方法在队列为空的情况下就会阻塞,从而避免了线程一直占用cpu的问题。
OK,将poll()方法换成take()方法,CPU瞬间正常!
大功告成?NO!然而却出现了另外的异常现象,获取到的日志对象并没有保存到数据库。这是什么原因呢。。。不管如何CPU爆表的问题算是解决了,至于日志对象没有保存到数据库是为什么,那就是另外的问题了,也跟线程相关,那么且听下回分解。
最近参与到一个新项目的开发当中,项目的大部分功能已经由另外一名同事实现,这名同事给我反映了一个问题,说每次部署这个项目成功之后CPU立马飙到将近百分之百,一直没发现问题在哪里。
2.解决过程
听了同事的描述之后,心想肯定是程序性能问题,由于之前一直没碰到过这类问题,怀着浓厚的好奇心想一探究竟,解决这类问题本省也是成长,说干就干。
我首先想到的解决这类问题是使用性能分析工具——Java VisualVM,JDK自带的可视化工具。
通过VisualVM可以看到,除了CPU爆表之外其他都比较正常。
再看看线程运行情况,按运行时间排序,发现占用CPU最多的线程如绿色部分,其中有RMI相关线程、I/O线程。
看到这个排名,首先怀疑是RMI远程调用相关问题,由于项目中使用了Dubbo等框架,再加上性能分析经验不足,一直以为问题的产生跟dubbo相关,然后就去项目中去掉dubbo相关的功能(虽然有点二,当时觉得值得试一试),花了不少功夫终于去掉dubbo可以重新部署了。
重新部署项目后,发现CPU突然又飙升上去了。显然与dubbo关系不大,接下来的怀疑对象就是OpenapiMonitorAsyncWriteLogThread这个线程,由于并不是我写的代码,所以并不了解这个线程的作用。我们接着生成线程的Dump文件:
接着看Dump文件,找到OpenapiMonitorAsyncWriteLogThread这个线程的相关信息,我们就可以跟踪到相应的代码了
到这里我们只是怀疑OpenapiMonitorAsyncWriteLogThread这个线程有问题,并没有确定,那么我们再看看抽样器对CPU抽样(如下图),发现这个线程中的pollOne()方法占用CPU很多,所以我们更加怀疑这个线程,所以还是值得去仔细研究一下这个线程中的代码。
看了代码之后发现,这个线程实现的功能就是在项目部署后创建一个线程,这个线程不断的向数据库中保存访问请求日志,这些日志对象首先放到一个队列当中,然后线程不停的从queue当中取出对象:
/** * 获取待插入记录list * @Title: pollBatch * @param batchSize * @return * @throws ParseException * @throws InterruptedException */ private List<OauthLog> pollBatch(int batchSize) throws InterruptedException { List<OauthLog> retList = Lists.newLinkedList(); int count = 1; while(count < batchSize){ OauthLog data = pollOne(); if(data != null){ retList.add(data); } count++; } return retList; } /** * 获取待插入记录 * @Title: pollOne * @return * @throws InterruptedException * @throws ParseException */ private OauthLog pollOne() throws InterruptedException { return queue.poll(); }
先前我们怀疑的pollOne()方法很简单,就是一个调用queue.poll()(这个queue是用的LinkedBlockingQueue)。我们去看看这个poll()方法的源码有什么特别之处呢:
经过仔细琢磨,终于发现问题。
int count = 1; while(count < batchSize){ OauthLog data = pollOne(); if(data != null){ retList.add(data); } count++; }
这个循环会一直调用pollOne()这个方法,也就是上图中的poll()方法,然而当queue为空的时候,那么poll()方法就会直接返回,继续回到while循环当中,如果while循环能够终止那么也不会有问题,关键是while循环所在方法一直在运行,如下图:
从而While循环所在方法会一直运行,所以问题就出在这里,到此为止我们已经找到了问题所在,就是从queue中获取日志对象的方法不管queue是否为空都会一直运行,就导致了CPU被这个线程一直消耗。
3.问题解决
我们上面已经知道了问题之所在,现在要做的就是解决问题。既然poll()方法在队列为空的情况下还会一直继续运行,那么我们就不应该用此方法来获取队列中的对象,而是使用take()方法。
显然take()方法在队列为空的情况下就会阻塞,从而避免了线程一直占用cpu的问题。
OK,将poll()方法换成take()方法,CPU瞬间正常!
大功告成?NO!然而却出现了另外的异常现象,获取到的日志对象并没有保存到数据库。这是什么原因呢。。。不管如何CPU爆表的问题算是解决了,至于日志对象没有保存到数据库是为什么,那就是另外的问题了,也跟线程相关,那么且听下回分解。
相关文章推荐
- 选定虚拟主机 性能凸显优势
- 修改一行代码提升 Postgres 性能 100 倍
- redis的hGetAll函数的性能问题(记Redis那坑人的HGETALL)
- 推荐Sql server一些常见性能问题的解决方法
- 我的服务器SQL2000的sqlserver占用了90%的cpu,怎么查是那个库?
- SQL Server误区30日谈 第9天 数据库文件收缩不会影响性能
- 和表值函数连接引发的性能问题分析
- SQLServer 2000 升级到 SQLServer 2008 性能之需要注意的地方之一
- MySQL服务器进程CPU占用100%的解决方法
- 数据库性能优化三:程序操作优化提升性能
- C#获取CPU编号的方法
- VBS中的字符串连接的性能问题
- 使用MySQL Slow Log来解决MySQL CPU占用高的问题
- 笔记本下什么是迅驰处理器(cpu)相关资料第1/2页
- mysql 性能的检查和调优方法
- 数据库性能优化二:数据库表优化提升性能
- SQL语句性能优化(续)
- SQL语句优化提高数据库性能
- 如何用分表存储来提高性能 推荐
- doscan.exe进程占用了大量的CPU和内存的解决方法