- 优百发布 >
- 资讯详情
千里之堤,溃于蚁穴
千里之堤,溃于蚁穴。这句话大意指一个工程巨大的堤坝,如果其中出现了被蚂蚁筑巢的洞穴,当在特定的条件下,也会存在被摧毁的风险。这常常用(yòng)来形容生活中因小(xiǎo)失大,由小(xiǎo)问题逐渐酿成大风险的情况。借用(yòng)它来形容程序漏洞所造成的隐患或严重后果,也是非常的贴切。
下面是筆(bǐ)者近期在排查一个服務(wù)器的相关问题时,所经历的真实事件和经验感受。
某天,项目组收到来自客户方关于系统问题的消息,现象是某系统服務(wù)无法进行访问,并且因系统属于企业的核心系统,问题需要紧急解决处理(lǐ)。当时头脑中的第一反应是诧异的,“因為(wèi)这个系统已经持续正常运行了一年多(duō)的实际,虽然出现过一些小(xiǎo)的问题,怎么会突然导致系统服務(wù)宕机?”。与客户方的现场人员进行了一番沟通与询问,诸如:最近是否有(yǒu)人动过服務(wù)器?是否有(yǒu)人做过其他(tā)什么异常操作?但都告知一切正常,并无其他(tā)异常情况发生。
既然没有(yǒu)出现过异常操作,还是先排查导致问题的原因吧,于是经历了下面的排查过程。
此项目部署在tomcat容器下,项目地址无法打开,直接访问tomcat的8081端口也没有(yǒu)响应,先查看端口情况:netstat -an |grep 8081
发现堆积了很(hěn)多(duō)连接,应该是很(hěn)多(duō)客户端连接过来,服務(wù)器无法处理(lǐ)请求,都在排队等待中。
再看看磁盘和内存情况:
l磁盘占用(yòng)情况:df -h
l内存占用(yòng)情况:free -h
发现内存和磁盘占用(yòng)空间都已经非常高了,判断有(yǒu)可(kě)能(néng)这里就是引起问题的原因。于是先清理(lǐ)服務(wù)器上过期的日志(zhì)文(wén)件、备份文(wén)件,将缓存释放。
l释放缓存
echo 1 > /proc/sys/vm/drop_caches
echo 2 > /proc/sys/vm/drop_caches
echo 3 > /proc/sys/vm/drop_caches
重启tomcat服務(wù)。不出所料,系统又(yòu)可(kě)以正常访问了,心里暗自窃喜……于是联系客户汇报问题已经解决,并通知用(yòng)户继续使用(yòng)。但是,没想到的是,短短一个多(duō)小(xiǎo)时后,又(yòu)收到了来自客户那边的反馈,服務(wù)又(yòu)挂了!!!
啊?!!!这又(yòu)是什么情况???脑子里顿时充满了无数问号!!!
看来缓存并不是问题的根源,只是其中的一个表象,问题并没有(yǒu)想象的这么简单。还得从tomcat的配置入手看看,会不会是tomcat的内存不足呢(ne)?于是进行查看,并给tomcat的配置文(wén)件增大了内存。
SET JAVA_OPTS=-Xms512m -Xmx1024m
默认的内存设置是128MB,可(kě)用(yòng)的最大内存设置是1024MB。先尝试修改為(wèi)初始化内存為(wèi)512MB。
保存,重启,心想这下应该可(kě)以了吧… …
系统又(yòu)可(kě)以访问了,有(yǒu)了一次上面的经历,觉得这次还是不能(néng)够大意,还得再观察观察… …于是每过十分(fēn)钟就对系统进行一次查看,确定系统是否正常,一个小(xiǎo)时过去了,没有(yǒu)出现什么问题,过了两个小(xiǎo)时,再检查也是正常状态。
到了下午,虽然系统已经可(kě)以正常使用(yòng)了,但总感觉哪里不对!“简单的增加内存,真的能(néng)彻底解决问题吗?是不是又(yòu)是表象,治标不治本呢(ne)?系统好好的為(wèi)什么会突然瘫痪?明明刚清理(lǐ)的内存,為(wèi)什么内存会再次爆满?到底是什么原因导致的内存飙升?”这些问题都还没有(yǒu)找到答(dá)案,我有(yǒu)预感,系统还会再次出问题。果不其然,不久,系统再次无法访问了!
看来问题必须深究,到底是哪里出了问题。外部的环境因素已经排除,问题就很(hěn)有(yǒu)可(kě)能(néng)出在程序内部,这只是感性的判断,但问题究竟是怎么发生的现在还一无所知。程序中的问题该怎么找?无法进行问题场景地再现是程序调试的最大障碍。既然无法进行情景再现,只能(néng)从日志(zhì)入手,慢慢地分(fēn)析并查找问题了。日志(zhì)文(wén)件有(yǒu)100M!!!查看起来还是很(hěn)费功夫的,使用(yòng)搜索查看报错信息,结果发现了下面这个报错信息:
原来是他(tā)“OutOfMemoryError”!终于找到问题的根源了。找到了错误,问题就解决了一大半,接下来就只需要找到是什么原因导致的OutOfMemoryError。在报错的日志(zhì)部分(fēn),继续向上翻看日志(zhì),看能(néng)不能(néng)找到是哪个操作导致了错误的发生,发现了巨長(cháng)的查询结果集,根据结果集的信息,找到了隐藏在日志(zhì)缝隙中的那条sql语句:
SELECT DIS_ID, IU_ID id,IU_CODE, FULLNAME IU_FULLNAME, STATE,SYS_ID FROM CODE_NBDW_DIS WHERE STATE = '0' ORDER BY IU_CODE
这是一条查询待分(fēn)发数据的sql语句,仔细一看便能(néng)看出问题的端倪,这条sql语句并没有(yǒu)对结果集做限定,如果STATE = '0' 的数据有(yǒu)1000条,应该是没什么问题,但如果有(yǒu)100000+甚至更多(duō),结果会怎样,就会出现今天这种情况OutOfMemoryError。
原来就是这么一条没有(yǒu)加限定条件的sql语句,就导致了整个系统的崩溃,真是“千里之堤,溃于蚁穴。”。在起初开发系统程序时,程序这么写可(kě)能(néng)并不会出现问题,那时因為(wèi)数据量相对还比较小(xiǎo),问题不会暴露,但当数据量剧增时,带来的结果就是灾难性的。
所以,写代码,不只是算法和逻辑,一定还需要多(duō)一些思考,用(yòng)发展的思维去考虑问题,各种可(kě)能(néng)发生的情况,都要提前考虑到,保证程序的健壮性。
【健壮性的思想】
1、正常运行的代码首要追求高效性
这个“高效性”如果从逻辑的角度来解释,那么一方面是“高效”地对正确的数据执行正确的算法(方法/策略),另一方面是“高效”地找出异常,然后丢给异常处理(lǐ)代码去处理(lǐ)。
2、处理(lǐ)异常的代码首要追求健壮性
就是程序必须能(néng)从异常中自我恢复。由于代码多(duō)数时间跑的是“正常”逻辑,少数情况下才不得不处理(lǐ)“异常”,所以“异常”处理(lǐ)的代码中,首要任務(wù)是健壮,跑不死,而高效性则是次要的。
【如何提高健壮性】
1、尽量少用(yòng)静态变量
2、尽量避免在类的构造函数里创建、初始化大量的对象
防止在调用(yòng)其自身类的构造器时造成不必要的内存资源浪费,尤其是大对象,JVM会突然需要大量内存,这时必然会触发GC优化系统内存环境;显示的声明数组空间,而且申请数量还极大。
3、对象池技术
尽量在合适的场景下使用(yòng)对象池技术以提高系统性能(néng),缩减开销。但是要注意对象池的尺寸不宜过大,及时清除无效对象释放内存资源。综合考虑应用(yòng)运行环境的内存资源限制,避免过高估计运行环境所提供内存资源的数量。
4、大集合对象拥有(yǒu)大数据量的业務(wù)对象的时候,可(kě)以考虑分(fēn)块进行处理(lǐ),然后解决一块释放一块的策略。
5、不要在经常调用(yòng)的方法中创建对象,尤其是忌讳在循环中创建对象。
可(kě)以适当的使用(yòng)hashtable,vector创建一组对象容器,然后从容器中去取那些对象,而不用(yòng)每次new之后又(yòu)丢弃。
6、一般都是发生在开启大型文(wén)件或跟数据库一次拿(ná)了太多(duō)的数据,造成Out Of Memory Error的状况,这时就大概要计算一下数据量的最大值是多(duō)少,并且设定所需最小(xiǎo)及最大的内存空间值。
7、尽量少用(yòng)finalize函数
因為(wèi)finalize()会加大GC的工作量,而GC相当于耗费系统的计算能(néng)力。
8、不要过滥使用(yòng)哈希表
有(yǒu)一定开发经验的开发人员经常会使用(yòng)hash表(hash表在JDK中的一个实现就是HashMap)来缓存一些数据,从而提高系统的运行速度。比如使用(yòng)HashMap缓存一些物(wù)料信息、人员信息等基础资料,这在提高系统速度的同时也加大了系统的内存占用(yòng),特别是当缓存的资料比较多(duō)的时候。
其实我们可(kě)以使用(yòng)操作系统中的缓存的概念来解决这个问题,也就是给被缓存的分(fēn)配一个一定大小(xiǎo)的缓存容器,按照一定的算法淘汰不需要继续缓存的对象,这样一方面会因為(wèi)进行了对象缓存而提高了系统的运行效率,同时由于缓存容器不是无限制扩大,从而也减少了系统的内存占用(yòng)。现在有(yǒu)很(hěn)多(duō)开源的缓存实现项目,比如ehcache、oscache等,这些项目都实现了FIFO、MRU等常见的缓存算法。
以上就是筆(bǐ)者在工作中所经历的一次系统问题事件的整个过程,以及所引发的思考。是对这次事件的一个全面的记录、分(fēn)析和总结,希望能(néng)给各位读者带来些有(yǒu)益的经验和借鉴。