记一次服务器宕机问题查找

梦浪的小虾米
2021-11-12 / 0 评论 / 2,274 阅读 / 正在检测是否收录...

1、服务转到我手中维护后发现服务会间隔性宕机。
2、询问之前维护人员情况被告知是由于攻击并发量高导致连接被占满,服务器拒绝服务导致的。
3、遂按此原因进行整改,添加限制逻辑使攻击者的连接快速失败。
4、在深夜测试后上线。
5、上线后客户端大规模报使用异常。
6、回滚代码,查找原因。
7、下载、整理筛选相关日志出来,统计调用频次,统计接口调用参数。
8、发现服务宕机时会连续报数据库连接池获取连接失败。深入探查代码发现数据库组件的所有代码都会将异常吞了,并且未重新抛出异常,只是e.printStackTrace(),由于使用了日志组件,e.printStackTrace()不会打印在日志中,所以数据库操作的所有日志都获取不到,异常也不会抛出到调用层,只能通过返回结果判断执行情况(这是此公司自研数据库组件的重大失误)。
9、通过多次服务宕机时的日志对比发现,宕机前会有大量的请求无法正确结束(但是由于日志太过粗略,无法获知非正确结束的请求的失败原因合返回参数)。
10、统计调用频次发现,并发量并不高,在宕机前几分钟频次只有大约5次/s。在出现非正常结束请求后并会发飙升到30次/s。查看代码,并未发现高耗时的逻辑,并且这个并发量在单机系统中也并不高,查看代码的并发逻辑,排除由于锁等原因导致宕机。
11、通过客户端模拟操作,发现客户端新登录和唤醒操作均会调用此服务,但是之前的维护人员只知道登录会调用。而且唤醒操作时如果调用此服务失败,就会以每秒一次的频率不断重试,所以判断这是服务宕机前接口调用频次飙升的原因,排除有人故意高并发调用导致宕机。
12、由于日志打印不全,无法获取错误的具体日志。猜测是否是数据库被锁表,然而在一次宕机后查询数据库的连接日志,未发现锁表的进程。所以排除数据库锁表。
13、观察日志,发现请求在非正常结束前出现了一次数据库异常,有错误日志,SQLException
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Can’t call rollback when autocommit=true。查看代码发现捕捉错误后只是打印此日志,并未对数据库的连接进行进一步的处理(未关闭数据库连接),查找前几次的宕机日志均发现此现象,到此基本确定宕机原因:抛出此异常后数据库连接未正确关闭,导致可用的数据库连接越来越少,到最后所有连接被占满。
14、问题复现:将数据库连接减少到5个,故意调用会引起数据库SQLException的接口5次,果然引发了宕机。
15、修改代码,修改导致MySQLNonTransientConnectionException异常的代码,并且添加了关闭数据库连接的代码,上线。问题解决。

总结:
1、错误的日志十分总要,杜绝e.printStackTrace(),日志中几个元素必不可少:
(1)、时间,越精确越好
(2)、线程名称,区别越明显越好
(3)、打印日志的位置,越精确越好,一般loger会标明打印日志的类名
(4)、日志提示语,区分越明显越好
(5)、请求的入参和返回结果也很重要,如果日志负荷不重的话应该打印出来。
(6)、我支持使用异常来完成业务中错误的流转,反对使用状态码。比如在执行sql的时候发生了连接错误,可以通过重新包装异常,将异常直接一层一层抛到最上层的调用,然后统一打印日志,根据统一的请求返回规范返回错误码,这样方便统一处理,业务代码逻辑清晰。
(7)、日志分片,合适的分片时间或者大小,一般是以时间和日志大小为准,常用的是1天/10M,我认为1天/100M也可以。
2、所有的连接池、mysql连接池、redis连接池等等,一个很重要的问题就是使用后连接的归还与关闭,在使用框架的时候要注意。
3、对于日志中的异常要敏感,因为一个异常很可能会导致其他不明显关联的地方出现异常。
4、不可轻信他人的判断,很可能是误导。

转载地址:https://blog.csdn.net/u011649691/article/details/103953422

0

评论 (0)

取消