背景:约100万月活跃用户,APP版本BUG(至少4万用户死循环访问,堪比DDOS),修复历时7天7夜。
世界杯前夕,服务器拒绝访问,线程挂死,导致大量请求拒绝访问,少量能访问,但访问时间缓慢导致超时,相当于服务崩溃。
通过监听端口有效的访问连接数,最高可达1000并发。
netstat -an | grep ESTABLISHED | wc -l
访问量已经是无法控制,只能从应用入手,强制更新有bug的APP版本,把有BUG的版本的所有接口全部拒绝访问,以免影响其他版本的访问,但效果甚微。
我怀疑此时的并发量已经远远超出现有服务器的压力,如果并发量超出服务器的承受范围,那无论如何优化代码都是无事于补。为了验证服务器的极限,把所有接口都屏蔽(改成立刻响应,几乎不消耗太多的资源),经过测试发现,每开启入口,线程池立刻满上,无论如何调大入口数,最大连接数,系统文件最大打开数(Linux ulimit)等参数,到达某一瓶颈值就立刻线程挂死,拒绝访问,此时系统资源如CPU、内存、IO等系数都消耗不大,由此推断我们web容器已经到达了瓶颈。
现在只剩下一个方案:扩容。扩容有两种方式,垂直扩容,横向扩容。垂直扩容相对需要时间有点长,需要安装节点,添加节点等操作,还需要测试,还会存在多种不确定风险,毕竟对现网服务器动手脚。横向扩容是最佳方案,因为有现成的集群服务器可以用(另外项目已经停止,服务器空闲),可以通过现网服务器集群的上一层做分流(F5),把一半的流量分过来新集群。于是我们重新部署了应用到新集群服务器。开启分流入口,系统业务恢复,访问正常。
好景不长,大概运行了半天时间,发现web容器线程忽然飙升到某一瓶颈值大部分挂死,拒绝访问。我又开始怀疑难道扩容都还不能支持现在的压力,经过了强制更新和旧接口的拒绝访问,性能消耗应该有所下降了。但发现有一个奇怪的问题,线程一开始是正常的,就是运行到某一个时间段是忽然慢慢上升,直到瓶颈值挂死,我开始怀疑应用同样存在瓶颈,开始向代码入手。由于历史原因,查看代码也是一件要命的事情,在这里消耗还不如查看运行日志,但同样是历史遗留问题,日志的错误一大堆,之前是因为不影响业务运行就不管,因为以前系统从tomcat迁移到was的,tomcat的兼容性好,没有什么错误,但是迁移到was后就报出一大堆问题,可能是was的要求比较高,但是后来没人改修改,不影响现网运行就不理。所以现在从日志入手也是一件很痛苦的事情,但这是没办法中的办法。把一个10M的日志文档拿下来,一个个错误排除。功夫不负有心人。终于让我发现了点苗头。发现了线程挂死的接口,后来经过了代码的排查,终于找到问题所在,是因为这个接口用到了synchronized,难怪线程会挂死,之前不会发生死锁的原因访问量不大,现在访问量通过推广和那个致命的BUG,不挂死才怪。
后来经过了代码的修改和完善,把所有有用到了同步的都修改了,重启应用打开。应用访问正常、线程正常、但是数据量CPU消耗有点大。通过对数据库的排查,发现,是应用的业务逻辑很多没有梳理好,SQL的许多的硬解析导致了数据库CPU消耗比较高,但这是历史遗留问题,现在一个个是非常不现实的,我们只能对消耗较高的SQL进行完善。
经过几天的系统运行,相对稳定,就是数据库CPU消耗时高时地,这个应用的推广活动有关,但还不至于导致服务器倒闭或挂死。
系统稳定后对现网的所有日志等数据进行分析,发现我们APP的用户数增长比较快。如果安装现在的增长速度,必须还的提前申请服务器扩容(系统客户可以重视和给力)。
现网的系统架构是当初以一个“企业应用”建立的,还包括了许多历史遗留问题,只能说too young too simple。
现在已经是百万级用户的APP了,看来,架构需要重新设计和规划了。其实万事具备,只欠东风(客户的重视度)。我只能说,系统≠软件+硬件,系统=软件+硬件+沟通+企业政策+任何关联因素。
20140613故障处理纪要