从Groovy到Java性能

在去年年初换过工作开始专注做性能测试,其中有一项很大的挑战就是FunTester测试框架性能是否可以支撑公司现在的业务。之前有文章分享过如何突破职业瓶颈,其中讲到如何负责服务的QPS上升一个数量级是一个非常大的挑战。最近在这个问题上,我有了一些新的发现,分享一下自己对FunTester测试框架使用的Java+Groovy这对组合性能方面的认识。

首先谈谈Java语言的性能,在此之前我在本地做过一系列的单机测试10万QPS,K6、Gatling和FunTester终极对决!单机12万QPS——FunTester复仇记,对此我还是挺满意的。后来在工作用应用得到了单个进程6w的QPS,也算是符合预期了。

为什么说是算是符合预期呢?因为在开始写FunTester性能测试框架决定使用Groovy时,就觉得这个脚本语言性能肯定是有限的,比Java肯定差得远。因为本质上Groovy启动的还是Java进程,可以通过优化JVM启动参数来提升性能,堆一些硬件也能获取非常不错的性能,再加上本机测试最高能达到12万QPS的情况来说,问题还是在可控范围内的。

然后在某次单机性能测试QPS要求到了5万,出现了一些异常现象,我就开始着实优化这个Groovy JVM的启动参数,可是让人绝望的是根本找不到资料。官方也没有提供相关的API参考。不过没关系我还有别的方式启动Groovy脚本用例。那就是先用Java启动一个JVM,然后加载Groovy脚本,通过修改Java进程的JVM启动参数依然可以控制整个JVM所能使用的资源。当然这个方案会损失一部分Groovy的灵活性。

到了这个程度只能走一步看一步了,只能暂时相信备选方案。为了缓解这方面焦虑,我还特意学习了Go语言,又把FunTester做过的测试类型重新写一遍Demo,一旦Java性能到了瓶颈,我还有planC。

不过在最近的实践中,这种担心的的确确不存在了,在实际业务测试中,Groovy单进程实现了11万的QPS,CPU使用率1200%,堆内存使用16G,3s一次YoungGC,测试过程无FullGC,简直完美。而且Groovy特性,可以随时启动多个进程,实现手动分布式了,50万QPS没啥压力了。

下面分享一下最深刻的三点感触: 1. 分布式:非必要不要自己搞分布式,尽量选择成熟方案。单纯从性能角度没有必要,一些特殊场景需求,比如多地域测试,会优先考虑分布式方案。 2. Java单进程拥有足够的性能,堆硬件也能提升很高的单机性能。应对10万QPS级别的HTTP接口性能测试完全没有问题。这个数据包括MySQL、Redis、RPC测试。 3. Groovy性能也是足够的,这里包括主要是能够使用的物理资源,基本等同于Java性能,而且拥有随时进行人工多进程或者人工分布式的能力。同时也提供自动测试的能力,包括进行分布式部署执行的能力,但是目前在10万QPS级别上没有必要。 4. HTTP连接池数量,这个很负责任分享经验:HTTP连接的使用量等同于并发线程数,如果不出错基本可以保障这个等式。

PS:针对最后一点多说两句,如果预估的平均响应时间50ms,那么如果要达到10万QPS,就需要5000个线程,一般我会设置8000最大值,因为使用线程池是随用随加不必单线创建多不用线程问题。那么预估HTTP连接的使用量就是5000。而且根据实际测试结果,5000线程池活跃线程的量上,系统花在上下文切换的CPU资源并不多,上千线程使用上,并不用过多担心CPU消耗过多的问题。

回到Go语言的话题,我的经验就是非必需不要学,好好专深学习一种语言生态是后续发展的基础。而且编程语言很多都是相同的,举一反三绝不是空穴来风。我对Go语言的使用还是仅限于基础学习,简单使用,能够阅读代码的程度上。在实际的性能测试中,Go语言的性能优势体现在内存上,CPU几乎没有优势。由于Go语言可以在脚本情况下随意执行某个方法的能力,所以灵活性上上层,这一点远超Java,等同于Groovy。

最近Go语言学习进展停滞不前,后面输出相关内容会变少。

Have Fun ~ Tester !