问题
昨天刚好是周五,忙碌了一周本以为可以在周五好好轻松下,没成想线上的活动服务出了个问题,市场反馈最近上线的微信活动(是一个类似于测试性格的答题游戏),在游戏结束后结算的时候页面卡死。
排查
我们赶紧查看接口调用日志,发现接口平均响应时间在4s左右。这次活动使用了mongodb,我们之前在做活动的时候碰到过一次忘记给mongo建立索引,导致接口卡死的情况,所以赶紧检查mongo的查询top情况,发现并没有耗时的请求。
这时一个同事提醒我们活动接入了druid,可以检查下是否是sql的问题,我们赶紧打开druid的控制台查看,最慢的sql耗时在800ms,但也不至于把接口拖到4s的地步,不过还是针对索引做了优化。可是情况并没有好转,时间已经过去了十几分钟。
那看来不是存储的问题,那就去找服务器的问题,我们就去看服务器的CPU,内存,网络,发现服务器的CPU达到了100%。
问题总算找到了,本着优先保障线上使用的目标,在问题得不到很快定位的情况下,我们决定再弹出一台服务器,替换掉目前线上有问题的服务器,这样也可以保护问题现场,帮助我们排查问题。
这个时候我们从线上dump出线程dump,但是从中并没有发现什么有用的信息。
新的服务器挂到SLB上之后,我们观察了下,它的CPU很快也达到了100%,这时我们也还没有找到问题出在哪里,由于之前的服务器已经没有流量进来了,我们尝试着重启了这台服务器,然后访问这个活动,观察日志的输出,发现开始的时候一切都是正常的,但是最后的结算接口调用后CPU直接达到了100%,那就是说程序中出现了死循环,导致线程卡死了得不到释放。
问题知道之后我们就去看这个接口的逻辑,发现 是一个while循环,然后里面一个很复杂的逻辑,在询问了对应开发人员这段逻辑之后,我们在不能很快定位原因的情况,写了一段简单的替换逻辑,虽然不能完整的实现原先的逻辑,但是用户使用上并没有太大的区别。
解决
重新上线后,重新试了下流程, 没有再出现CPU100%的问题了,至此问题解决。
总结
在晚上的例会上我们分析了原因,是代码提交之后没有完整的code review流程,导致代码没有检查就提交测试,而有些bug在测试环节是很难发现的。
经验
关于问题排查
线上问题检查基本流程是
CPU,内存,网络
数据库索引(包括nosql)
日志
线程dump,堆dump分析
关于代码编写
针对本次bug是因为while循环没有正常退出导致的,所以业务中应该尽量避免使用while循环,尤其是业务复杂的时候,可能考虑不全,不能保证循环一定能够退出。这个时候应该使用可预见的有限for循环+if退出条件来代替while循环(web引用几乎没可能有需要无限循环的业务)。
其实代码在一开始就进行code review,可能就能够避免很多潜在的问题,而且尽量不要写一些太复杂的业务逻辑,如果真的很复杂,极有可能是这个实现方式就不正确,可以考虑下有没有替换方案。
领取专属 10元无门槛券
私享最新 技术干货