应公司需求我们对一个项目进行了线上压力测试,结果发现,三台服务器一共只有59TPS,结果惨不忍睹。
那么针对这样的场景,我们利用一周时间进行专注性的优化,寻找性能的瓶颈点。
第一步:我们针对线上的环境进行模拟,尽量真实的在测试环境中再现,采用数据库连接池为咱们默认的C3P0。
那么当压测到二万批,100个用户同时访问的时候,并发量突然降为零!报错如下:
Could not get JDBC Connection; nested exception is java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
针对以上错误跟踪C3P0源码,以及在网上搜索资料(http://blog.sina.com.cn/s/blog_53923f940100g6as.html)发现,C3P0在大并发下表现的性能不佳。
第二步:针对这个问题进行数据库连接池优化,更换了BoneCPDataSource,以及Apache BasicDataSource后,发现报错如下:
java.lang.OutOfMemoryError: unable to create new native thread, dubbo version: 2.5.4, current host: 192.168.122.1 java.lang.OutOfMemoryError: unable to create new native thread
第三步:由此可以判断,问题不在于连接池的问题,于是在压测的时候,将DUMP日志导出进行分析发现,项目中启动了一万多个线程,而且每个线程都极为忙碌,彻底将资源耗尽。
于是迅速定位到代码,发现如下代码:
private static final ExecutorService executorService = Executors.newCachedThreadPool(); /** * 异步执行短频快的任务 * @param task */ public static void asynShortTask(Runnable task){ executorService.submit(task); //task.run(); } CommonUtils.asynShortTask(new Runnable() { @Override public void run() { String sms = sr.getSmsContent(); sms = sms.replaceAll(finalCode, AES.encryptToBase64(finalCode, ConstantUtils.getDB_AES_KEY())); sr.setSmsContent(sms); smsManageService.addSmsRecord(sr); } });
那么问题到底在哪里呢???就在这一行!
private static final ExecutorService executorService = Executors.newCachedThreadPool();
在并发的情况下,无限制的申请线程资源造成性能严重下降,在图表中显抛物线形状的元凶就是它!!!那么采用这种方式最大可以产生多少个线程呢??答案是:Integer的最大值!看如下源码:
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory); }
那么尝试修改成如下代码:
private static final ExecutorService executorService = Executors.newFixedThreadPool(50);
修改完成以后,并发量重新上升到100以上TPS,但是当并发量非常大的时候,项目GC(垃圾回收能力下降),分析原因还是因为 Executors.newFixedThreadPool(50)这一行,虽然解决了产生无限线程的问题,但是
当并发量非常大的时候,采用newFixedThreadPool这种方式,会造成大量对象堆积到队列中无法及时消费,看源码如下:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
可以看到采用的是无界队列,也就是说队列是可以无限的存放可执行的线程,造成大量对象无法释放和回收。
结论:
目前我们的项目还在持续优化中,还没有最终优化完成,目标是要把项目优化完善,但此次事件,再次提醒我们,在使用线程池的时候,一定要把握其细节,深入了解其原理再使用,不要随意使用,任何线程池的使用方式都有不同的使用场景,并不是只要使用了线程池就万事大吉,还有很多工作需要我们去注意。
相关推荐
一个Sqrt函数引发的血案-博文代码 博文地址:
编程趣谈:一个Sqrt函数引发的血案参考.pdf
一个脚本引发的血案.docx
子网掩码的注意事项,由掩码地址设置不当,引发的血案(网络访问故障)
第002章 一个输入框引发的血案.rarpython面试
古代汉语02-7.1 一个奇葩名字引发的血案:《左传》郑伯克段于鄢解题_28.pptx
一次理发引发的血案250字三年级作文范例.pdf
一条SQL引发的“血案”:与SQL优化相关的4个案例.docx
一台电脑引发的血案作文【初中初二500字】.docx
主要介绍了Nginx正确的404配置方法,本文根据实际案例总结而来,可以说是一个等号引发的血案!需要的朋友可以参考下
一条SQL引发的“血案”:与SQL优化相关的个案例(文末送书).docx
不过这个恰好被我撞见了,一个慢查询把整个网站搞挂了 先看看这个SQL张撒样子: # Query_time: 70.472013 Lock_time: 0.000078 Rows_sent: 7915203 Rows_examined: 15984089 Rows_affected: 0 # Bytes_sent:...
工程师故事,晶振引发的系列血案
sql学习 全局临时表案例1_统计信息引发性能血案.sql
防不胜防:一个空格在数据库里可能引发的N重血案.pdf
一个“玩笑”引发的“血案”--就“欧典危机”答《成功营销》 管理资料.doc
到现在还记得当年广大观众调侃吐槽冯小刚的夜宴时网上比较火的一个视频——一个馒头引发的“血案”(估计现在90后00后没看过这个,当时师弟师妹们还在初高中为考大学做准备呢,应该没有机会看个电影啥的,不过也不...
先来分析选型A,Integer i01 = 59,是一个装箱的过程,在进行i01 == i02的比较过程中,因为右边是整型,发生了拆箱的动作,所以进行了值得比较,所以返回true。 在这里拿出Integer a = 59,Integer b = 59,这种又会...
主要给大家介绍了关于MySQL中因一个双引号错位引发的血案的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧