Java内存溢出和连接池死锁等各种混乱问题的解决
概述:
在对后端java抓取微博数据系统从1.0升级到2.0的过程中,产生了一些严重的问题,经过调整后,目前这些问题未再出现。
问题1:
现象:前端页面数据混乱,A用户的数据混杂了B用户的数据,并且现象比较普遍。
原因:新浪基于JAVA语言SDK的V2版本存在BUG,有并发问题。
引起该问题的主要是SDK中的一个封装类:HttpClient。
因为该类为全局静态共享类,该类中的属性也同样成为全局共享类,在并发情况下,会导致部分属性的值被篡改,从而引起数据混乱。
解决办法:将该类中会导致并发问题的属性删掉,改为从方法参数中传入该值,改完后经反复测试,目前该问题未再复发。
问题2:
现象:后端抓取日志出现连接池死锁、长时间获取mysql连接失败以及内存溢出异常的现象。该现象比较混乱,各种错误混到一起,使得查找问题都不太容易。
原因:
1,把java数据库连接池框架c3p0的日志打开后,发现c3p0存在死锁问题。
2,部分SDK接口短时间内会返回大量的数据,可能因此而导致堆溢出。
3,代码中的预处理sql会导致长时间获取mysql连接失败。
解决办法:
针对问题1:更换连接池,经过一番调查和比较之后,选定淘宝开源连接池druid。经部署测试后,死锁问题未再出现。
针对问题2:
首先,错误日志显示为“java.lang.OutOfMemoryError: Java heap space”,于是增加java虚拟机的最大堆内存,后发现问题仍然存在内存溢出,
不过错误日志信息变为了“java.lang.OutOfMemoryError: GC overhead limit exceeded”。
其次,通过对错误日志的分析,发现每次出现该问题时,都会有“提到我的微博”等抓取任务在运行,
于是修改了“提到我的微博”和“个人微博信息”等业务中每页返回的微博数,并调小部分代码中涉及到操作数的地方。
同时修改carry端启动脚本,在java命令后加入参数“-XX:+UseGCOverheadLimit”,之后内存溢出问题消失。
针对问题3:这个问题找了很长的时间,调整了很多连接池的参数和java启动参数,并使用java内存监控工具分析和诊断。
最初认为是连接的空闲时间过长,空闲连接未被释放导致该问题,于是在druid连接池中加入一个参数“removeAbandoned”,
该参数会检测长时间未释放的空闲连接,若发现有这样的空闲连接并且空闲时间超过设定的时间,则会抛异常,并强行终止这种连接。
通过连续监控,并未发现预料中的强行终止异常,问题仍然重现。
最后通过对大量错误日志的分析,发现所有出现该问题的地方都会出现预处理sql,于是将代码中所有涉及到预处理sql的地方全都改成正常的普通sql,部署后问题未再出现。