小bug引发的蝴蝶效应



昨天在项目测试中遇到了一个比较奇怪的问题,先说一下项目结构和环境吧

项目结构:
类型:网络监控
部署方式:web层做展现,采集层做数据采集,分析处理层做数据分析和入库,三层独立部署,运行时通过ActiveMQ来进行通信

问题描述:由于是测试阶段,数据量不太大,有20多个采集任务在运行,本机测没发现有大的问题,在服务器上跑了一夜,第二天早上发现服务器ssh登陆不上,显示连接超时。用root登陆上去,发现采集层不再采集数 据,查看日志,最早开始报异常是MQ连不上然后就是snmp也开始连上不,最后发展到内存堆溢出。

问题解决及分析步骤:

1.查看ActiveMQ日志,无任何异常,分析处理层也无异常,表明MQ并未发生故障,重启采集层后又能正常连接,再次证明MQ正常运行,MQ故障可以排除,同时分析处理层故障也可以排除

2.web层:运行在tomcat下,重启采集层后,采集层向web层通过MQ发送请求采集任务消息,能被正常处理,WEB层故障可以排除

3.问题现在定位到采集层,因此,本机启动测试,用jdk自带工具查看采集层运行情况,发现内存基本上没有多大变化,查看对象也没有占用内存多的,因此,代码逻辑错误导致内存溢出也排除,最后通过观    察一段时间的线程,发现趋势如下

小bug引发的蝴蝶效应_第1张图片
 


每隔分钟,线程数会上升一个台阶,但我们任务调度的时候是用的quartz,里面的线程池是指定的,我们给了一个数据量级为50的线程池,通过观察发现,这50个线程是固定的,并没有增加,说明我们自己的线程池是安全的,没有发生泄漏,最终发现,随着时间不断增加,有个UDPListener线程不断在增长,但每次就长一到3个,也不太容易察觉,并且伴随UDPListener线程会有一个timer线程也是一一对应的增加,现在问题定位到了这个UDP的连接了,查看我们的采集进程 代码,用的协议各种各样的都有,最终定位到SNMP采集,虽然只写了这么几个字,但这个定位过程相当长,用了将近整个下午的时间,可能是与我经验不足有关吧,最后查看代码,发现有异常捕获,也有连接关闭,实在看不出问题到底出在哪,代码如下:

 long currTime = 0;
while (!sign.isFinishedSign()) {
try {
long unit = 100;
Thread.sleep(unit);
currTime = currTime + unit;
if (currTime >= timeout) {

throw new TimeoutException(target.toString()
+ ":Donnt retrieval table data of SNMP in "
+ timeout + " millisecond.");
}
} catch (InterruptedException e) {
}
}
snmp.close();
SnmpTable t = getTable(oidEntry, ls);
t.setBorn(bornDate);
 
当时一看,这段代码怎么看也是正常的,后来仔细分析,发现,那个snmp.close();只要超时,就永远也运行不到,也就是说,连接永远不会关闭。当时没仔细看,以为有try catch了,异常肯定能捕获,而没注意抛出的异常和捕获的异常类型不一样,这是一点;第二点,据我同事说,这样的写法,即使后面写的是

catch (Exception e)也是捕获不到的,只能在调用这个方法的外面捕获,这个我还没有测试。最终问题解决如下
while (!sign.isFinishedSign()) {
try {
long unit = 100;
Thread.sleep(unit);
currTime = currTime + unit;
if (currTime >= timeout) {
snmp.close();//加了这个
throw new TimeoutException(target.toString()
+ ":Donnt retrieval table data of SNMP in "
+ timeout + " millisecond.");
}
} catch (InterruptedException e) {
}
}
snmp.close();
SnmpTable t = getTable(oidEntry, ls);
t.setBorn(bornDate);
 
到此,问题全部解决,引发采集层故障的就是这么小小的一段代码snmp.close();,唉,当时这个模块还是我们项目经理写的,技术很强的,谁也没想到会犯这种错误,处理后的运行线程监控图如下:


小bug引发的蝴蝶效应_第2张图片

到此,问题是全部解决了,现在分析总结一下上面的现象,可以总结为:snmp连接没有正常关闭,导致UDP监听线程越来越多,线程不断占用socket连接,导致日志文件中记录的第一个异常发生—MQ连不上;随着时间的推移,线程越来越来,采集层对象也越来越多,最终导致内存堆溢出。至此,问题也全部分析完毕。

 

 

总结:看来我们还是不要太相信权威,呵呵 ,这算是一个典型的例子吧。再牛的人写的代码也会有bug,并且会有很低级的bug。呵呵 ,这篇文章写在这里是我自己用的备忘的,没什么技术含量,如果大家有什么好的相法和建议,可以随时和我联系,欢迎拍砖!

你可能感兴趣的:(数据结构,Web,quartz,activemq,ssh)