在去年年初换过工作开始专注做性能测试,其中有一项很大的挑战就是 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 !


↙↙↙阅读原文可查看相关链接,并与作者交流