高压力下表现不够好的Java应用

最近一个应用在做线上压测时,在压力比较高的情况下出现了恶化的现象,负载越来越高,响应时间越来越糟糕,并且无法恢复,而按照构建高可用系统的设计原则中的自我保护这个原则来说,这是一个设计的不够好的应用,来一起看看这个case。

这个应用之所以出现响应时间恶化的现象,现场排查的定位来看是由于GC变的很严重,基本上大部分的时间都在做GC,而GC严重的原因经过分析是由于活跃的线程比较多,每个线程占据一点内存,加起来的总数就导致了不断的GC,而不断的GC又导致了响应时间更糟糕,活跃的线程更多,占据的内存更多,GC更严重,这是一个典型的恶化的现象。

就这个case而言,要解决显然是要调小线程池的大小(后来确实也是调小了表现就正常了),避免创建的活跃线程太多,但活跃线程数到底应该控制在一个什么范围,这是一个很大的话题,并且如果每个应用都要配置,维护的成本将非常的高。

一个应用在高压力的状况下,最佳的表现应该是无论多高的压力都不至于导致应用出现恶化的现象,最多应该就是响应时间稍有下降,但在到达了一个值后应该就保持平稳,这样可以保障应用无论在多高压力下都是可以提供给部分请求正常服务的,对于其他超出处理能力范围的请求则拒绝。

自我保护说起来简单,但要做到确实不容易,意味着要考虑到各种异常情况下都得保障好应用本身还能尽可能的对外服务。

说到这个,顺带就说下另外两个自我保护做的不够好导致故障的case。

一个是某应用对外提供了批量接口,结果调用方一次性传入了上万个对象做批量查询,直接导致内存被耗光,fgc严重,从而影响到了正常的请求处理,这个case也是典型的自我保护做的差的现象。

另一个是Netty的API,对Netty熟悉的人会知道Netty Client的NioClientSocketChannelFactory是不能每次都new的,每次new会导致线程池重复的创建,但这个说起来我觉得也是自我保护做的不够好,导致在误用的情况下会耗光资源,影响正常服务。

其实这种case非常的多,mina/netty的WriteRequest队列其实也是的,一个没有限制大小的队列,在异常的情况下就会出现这个队列占据暴多内存,从而导致应用无响应的现象,而这些都必须靠使用者来避开。

所以其实在设计API的时候一定要考虑清楚怎么做好自我保护,而不是靠着文档,靠着代码的注释。

=============================
题图来源于:http://goo.gl/FdbRxm
欢迎关注微信公众号:hellojavacases

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。