生产项目中queue同步问题导致项目部署后CPU爆表问题解决
来源:互联网 发布:g代码编程 编辑:程序博客网 时间:2024/05/07 22:55
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当中取出对象:
/** * 获取待插入记录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爆表的问题算是解决了,至于日志对象没有保存到数据库是为什么,那就是另外的问题了,也跟线程相关,那么且听下回分解。
- 生产项目中queue同步问题导致项目部署后CPU爆表问题解决
- Eclipse中项目改名后在tomcat中部署问题
- 项目部署到tomcat Root中后导致 WebApplicationContext 初始化两次的解决方法
- 项目部署问题解决
- weblogic部署项目问题解决
- eclipse配置tomcat,以及部署项目到tomcat(解决项目部署后,webapps中无项目文件问题)
- 项目部署到weblogic后乱码问题
- svn中检出项目后无法部署
- Android Studio 添加依赖项目后导致运行项目卡死的问题解决
- eclipse中项目名称修改后部署到tomcat中应用名称仍然是旧的问题解决
- 解决加了@EnableHystrixDashboard后项目中freemarker配置失效导致404问题
- 项目中异常退出导致的问题。
- eclipse 中项目改名后的tomcat不能部署的问题
- [实训]解决eclipse中复制新项目改名后重新部署项目名不变的问题
- java项目中,关于svn上同步了setting文件 导致本地文件夹错乱的问题的解决
- eclipse中tomcat部署项目问题
- eclipse中tomcat部署项目出现问题
- 项目部署中遇到的问题
- 求助android studio
- springmvc如何设置多视图器,springmvc 多个 ViewResolver
- XML之命名空间的作用(xmlns)
- url、href、src详解
- IntentService源码分析以及HandlerThread的用法
- 生产项目中queue同步问题导致项目部署后CPU爆表问题解决
- Java 设计模式 适配器模式
- List Set Map(集合函数)
- iOS8 开发者手册
- spring简单原理
- ios笔记-NSSet
- android序列化及Parcelable使用
- @javax.ws.rs Webservice注解
- 【leetcode】83. Remove Duplicates from Sorted List