JMeter、K6、Locust横评(gRPC篇)

简介: 本文对比了JMeter、K6和Locust在gRPC接口性能测试中的表现,从脚本维护、资源占用、并发能力及结果输出等方面进行评估。各工具有适用场景,需根据需求选择。

 整体概述

       由于工作的原因,平时需要对服务的gRPC接口进行测试。之前一直使用JMeter编写Java请求的方式来测试的,但一直不清楚这种方式有什么局限。最近有时间看一些其他常用的性能测试工具(包括K6、locust),但发现网上的资料大多是http请求相关的。于是决定针对gRPC的请求进行一组简单的测试,计划从:脚本的维护难度、工具的资源占用、能支撑的并发数量和结果输出的完整性等方面进行比较。

       为避免过多的资源消耗,保持执行方式的统一,最终都选用无UI的命令行方式。其他基础信息如下:

  • 工具信息

image.gif

  • 环境信息

       使用两台 CentOS Linux release 7.9 的服务器进行测试,一台压力机一台服务机。

  • 服务信息

       使用一个Java API服务作为测试对象,接口使用gRPC协议,请求方式为POST,接口无状态,服务处理完成后直接返回结果。


测试内容

关于安装

  • JMeter

下载压缩包Apache JMeter,在Java环境运行即可。

  • JMeter grpc plugin:需要额外下载插件jmeter-grpc-plugin,下载之后放到lib/ext中即可。
  • JMeter Java request:需要将开发完成的jar放到lib/ext。
  • K6

执行命令安装Grafana k6 documentation,安装后即可通过命令运行。

sudo yum install https://dl.k6.io/rpm/repo.rpm 
sudo yum install k6


  • Locust

依赖Python环境,执行命令安装Locust

pip install locust


要执行gRPC请求,需要额外的依赖完成proto的解析gRPC Python

python -m pip install grpcio 
python -m pip install grpcio-tools


注意:Linux环境执行时,可能还需要安装protobuf。

关于脚本

  • JMeter grpc plugin

纯GUI的方式编写,可以说非常简单了,只需要按照接口规则传参即可,上手难度很低。

注意:把proto依赖路径填好,Listing就能选择请求方式了

image.gif

  • JMeter Java request

需要先编写Java调用代码,然后打成Jar,放到JMeter的依赖路径里。Java核心处理代码如下:

public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
        SampleResult sampleResult = new SampleResult();
        try {
            this.setupValues(javaSamplerContext);
            byte[] fileContent = fileList.get();
            ImageQualityDetectRequest imageQualityDetectRequest = ImageQualityDetectRequest.newBuilder()
.setRequester(Requester.newBuilder().setRequestSequence(RequestSequence).build())
                    .setImage(Image.newBuilder()
                    .setImageBytes(UnsafeByteOperations.unsafeWrap(fileContent)).build())
                    .build();
            sampleResult.sampleStart();
            ImageQualityDetectReply replies = stub.imageQualityDetect(imageQualityDetectRequest);
            sampleResult.setSuccessful(false);
            if (replies != null) {
                sampleResult.setSuccessful(true);
                sampleResult.setResponseCodeOK();
                sampleResult.setSentBytes(fileContent.length);
                sampleResult.setRequestHeaders(path + "\nindex:" + index);
                sampleResult.setResponseHeaders(replies.getApiCallInfo().toString());
                sampleResult.setResponseData(String.valueOf(replies), "UTF-8");
            }
        } catch (Exception e) {
            sampleResult.setSuccessful(false);
            sampleResult.setResponseMessage(String.valueOf(e));
            e.printStackTrace();
        } finally {
            sampleResult.sampleEnd();
        }
        return sampleResult;
    }

image.gif

之后就是在GUI中添加Java请求,选择需要调用的类方法,维护接口参数就可以了。

image.gif

  • K6

参考官方文档中的规则编写测试脚本,完成后使用命令运行脚本即可。主要测试代码如下:

export default function () {
  client.connect('192.168.0.30:8000', {
    plaintext: true,
    timeout: '30s',
  });
  const requestData = {
    image: {
      image_bytes: base64Data
    }
  };
  const response = client.invoke('quality.QualityService/Quality', requestData);
  check(response, {
    'status is OK': (r) => r && r.status === StatusOK,
    'status code is 2000': (r) => r.message?.response?.status === '2000',
    'result is true': (r) => r.message?.result === true,
    'score is over 80': (r) => r.message?.score > 80,
  });
  client.close();
}

image.gif

  • Locust

Locust进行gRPC协议的请求,是建立在Python对gRPC支持的基础上的,因此需要先将proto文件解析为py文件

python -m grpc_tools.protoc --proto_path=proto --python_out=proto --grpc_python_out=proto proto/quality_detection.proto proto/commons.proto

image.gif

测试运行脚本中加入gRPC调用的部分即可,测试脚本核心代码如下:

@task
    def image_quality_detect(self):
        start_time = time.time()
        
        try:
            request = QualityRequest()
            request.image.image_bytes = image_data
            response = self.stub.QualityDetect(request)
            response_time = int((time.time() - start_time) * 1000)
            if response.response.status == '2000' and response.result == True and response.score > 80:
                events.request.fire(
                    request_type="gRPC",
                    name="QualityDetect",
                    response_time=response_time,
                    response_length=0,
                    exception=None,
                    context={}
                )
            else:
                events.request.fire(
                    request_type="gRPC",
                    name="QualityDetect",
                    response_time=response_time,
                    response_length=0,
                    exception=Exception(f"Invalid response: status={response.response.status}, result={response.result}, score={response.score}"),
                    context={}
                )
        except Exception as e:
            response_time = int((time.time() - start_time) * 1000)
            events.request.fire(
                request_type="gRPC",
                name="QualityDetect",
                response_time=response_time,
                response_length=0,
                exception=e,
                context={}
            )
            raise

image.gif

请求能力

测试过程中依次增加10并发至60并发,每次运行5分钟,收集结果如下:

image.gif

       比较发现JMeter Java请求方式和K6的方式得到的TPS基本相同,只有0.72%的差距。不过K6的方式在低并发时TPS会比JMeter Java请求的方式要少一些,并发增大后,差距基本就没有了。使用JMeter grpc plugin的方式的话就会有24.65%的较大差距。而使用Locust的话,可能当前Python调用gRPC方式并没有实现真正的并发,暂时没有进行针对性的研究(如果有大神指点一下的话非常感谢)。

image.gif        响应时间的话选取的平均响应时间做比较,由于JMeter并没有收集测试执行文件,因此没有90%和95%这类数据,而K6的命令行执行结果自带这两项统计,算是不错的优势。Locust虽然有统计结果,但是因为QPS是瞬时大小。


服务资源使用

       当前服务主要性能瓶颈在CPU的使用上,这里以服务器CPU使用率来进行比较。

  • JMeter grpc plugin

image.gif

  • JMeter Java request

image.gif

  • K6

image.gif

  • Locust

image.gif

       通过以上几张图可以发现,使用JMeter Java request的方式可以使服务器的CPU持续运行在高位,相对波动最少。其次是K6的方式,服务CPU使用也很稳定,但在低并发时,CPU使用率会比JMeter Java request的方式略低,并且会有少量较大的使用率波动。使用JMeter grpc plugin的方式也能使服务器CPU在较高水平运行,但是有持续的波动,不会稳定维持在高位。Locust明显没有给服务增加并发压力,一直保持在单线程的水准。

资源消耗

       对于工具的资源使用情况,没有监控具体的进程,也是以压力机整个环境的使用情况为记录,粗略的比较CPU使用率和内存使用情况,如下:

  • JMeter grpc plugin

image.gif

image.gif

  • JMeter Java request

image.gif

image.gif


  • K6

image.gif

image.gif

  • Locust

image.gif

image.gif

       通过以上机组图可以发现,使用JMeter grpc plugin和JMeter Java request的方式都会在测试期间使用额外内存来处理gRPC请求,并在执行结束时回收内存。但是JMeter Java request使用的内存会比JMeter grpc plugin小。并且JMeter grpc plugin的CPU使用率会随着请求的增多而增多,而JMeter Java request可以通过代码来复用gRPC连接,仅在测试初期创建对象时会有较高的CPU使用,随后就维持在一个较低的水平。

       K6的方式和JMeter grpc plugin的方式相似,也是请求增多CPU使用率越高(应该是每次请求都重新创建gRPC连接)。但是由于K6天生对gRPC良好的支持,工具内存使用明显低很多。

       Locust因为并没有很大的并发和请求数,此处忽略。

一些问题

  • JMeter grpc plugin

当开始有并发时,控制台和jmeter.log日志中会出现错误(见下图),进而导致控制台无法输出执行结果。

protoc-jar: embedded: bin/3.21.4/protoc-3.21.4-linux-x86_64.exe

protoc-jar: executing: [/tmp/protocjar17733680174955940357/bin/protoc.exe, /home/tools/proto/quality_detection.proto,  /home/tools/proto/commons.proto,-I/home/tools/proto, -I/tmp/polyglot-well-known-types16154878821148829989, -I/home/tools/tool_com/abis_proto, --descriptor_set_out=/tmp/descriptor11234616606419094805.pb.bin, --include_imports]

  • JMeter Java request

需要注意Java请求中引入的依赖与JMeter中原有依赖可能存在的冲突问题。

  • K6

脚本无法复用gRPC client,必须在单次测试之内关闭client,否则会报错(见下图)

time="2025-09-23T12:24:41+08:00" level=error msg="GoError: context deadline exceeded: connection error: desc = \"error reading server preface: read tcp 192.168.0.31:57844->192.168.0.30:9000: use of closed network connection\"\n\tat reflect.methodValueCall (native)\n\tat default (file:///home/tools/k6_grpc.js:29:17(7))\n" executor=constant-vus scenario=default source=stacktrace

  • Locust

执行测试时增加用户数并没有使并发增加,而且多次测试时发现结果几乎一致,当前的调用gRPC的方式存在一定问题。


测试总结

结合前文的测试结果,主观性的评分如下:

image.gif 编辑

       以上内容仅供参考,虽然场景足够简单,但也有其他因素干扰结果。

       总体来说,每种工具方式都有自己的特点,在合适的场景中都有其独特的价值。如果哪位大神发现了文章中明显的错误,或者改良的方式,欢迎留言反馈。

相关文章
|
测试技术 持续交付 Apache
Python性能测试新风尚:JMeter遇上Locust,性能分析不再难🧐
【10月更文挑战第1天】Python性能测试新风尚:JMeter遇上Locust,性能分析不再难🧐
423 3
|
测试技术 数据库 UED
Python 性能测试进阶之路:JMeter 与 Locust 的强强联合,解锁性能极限
【9月更文挑战第9天】在数字化时代,确保软件系统在高并发场景下的稳定性至关重要。Python 为此提供了丰富的性能测试工具,如 JMeter 和 Locust。JMeter 可模拟复杂请求场景,而 Locust 则能更灵活地模拟真实用户行为。结合两者优势,可全面评估系统性能并优化瓶颈。例如,在电商网站促销期间,通过 JMeter 模拟大量登录请求并用 Locust 模拟用户浏览和购物行为,可有效识别并解决性能问题,从而提升系统稳定性和用户体验。这种组合为性能测试开辟了新道路,助力应对复杂挑战。
385 2
|
测试技术 持续交付 Apache
深度挖掘:Python性能测试中JMeter与Locust的隐藏技能🔍
【8月更文挑战第5天】随着软件规模扩大,性能测试对系统稳定性至关重要。Apache JMeter和Locust是两大主流工具,各有千秋。本文探索它们在Python环境下的进阶用法,挖掘更多性能测试潜力。JMeter功能强大,支持多种协议,可通过命令行模式执行复杂测试计划,并与Python集成实现动态测试数据生成。Locust基于Python,通过编写简洁脚本模拟HTTP请求,支持自定义请求及与Python库深度集成。掌握这些技巧可实现高度定制化测试场景,有效识别性能瓶颈,提升应用稳定性。
216 1
|
测试技术 持续交付 Apache
性能怪兽来袭!Python+JMeter+Locust,让你的应用性能飙升🦖
【8月更文挑战第5天】随着互联网应用规模增长,性能测试至关重要。本文介绍如何利用Python结合Apache JMeter和Locust构建高效可定制的性能测试框架。JMeter广泛用于负载测试,通过模拟大量虚拟用户并发访问来评估性能。Locust基于Python,通过编写简单脚本模拟HTTP请求,特别适合Web应用测试,比JMeter更灵活易扩展。Python作为胶水语言简化测试脚本编写并流畅自动化流程。文章提供JMeter命令行测试和Locust脚本示例,并展示如何用Python自动化执行和整合测试结果,最终帮助应用在高负载下稳定运行。
288 1
|
缓存 测试技术 Apache
告别卡顿!Python性能测试实战教程,JMeter&Locust带你秒懂性能优化💡
【8月更文挑战第5天】性能测试确保应用高负载下稳定运行。Apache JMeter与Locust是两大利器,助力识别解决性能瓶颈。本文介绍这两款工具的应用与优化技巧,并通过实战示例展示性能测试流程。首先,通过JMeter测试静态与动态资源;接着,利用Locust的Python脚本模拟HTTP请求。文中提供安装指南、命令行运行示例与性能优化建议,帮助读者掌握性能测试核心技能。
356 0
|
测试技术 持续交付 Apache
Python性能测试新风尚:JMeter遇上Locust,性能分析不再难🧐
Python性能测试新风尚:JMeter遇上Locust,性能分析不再难🧐
483 3
|
缓存 测试技术 Apache
告别卡顿!Python性能测试实战教程,JMeter&Locust带你秒懂性能优化💡
告别卡顿!Python性能测试实战教程,JMeter&Locust带你秒懂性能优化💡
322 1
|
测试技术 持续交付 Apache
性能怪兽来袭!Python+JMeter+Locust,让你的应用性能飙升🦖
【10月更文挑战第10天】随着互联网应用规模的不断扩大,性能测试变得至关重要。本文将探讨如何利用Python结合Apache JMeter和Locust,构建高效且可定制的性能测试框架。通过介绍JMeter和Locust的使用方法及Python的集成技巧,帮助应用在高负载下保持稳定运行。
207 2
|
缓存 测试技术 Apache
告别卡顿!Python性能测试实战教程,JMeter&Locust带你秒懂性能优化💡
【10月更文挑战第1天】告别卡顿!Python性能测试实战教程,JMeter&Locust带你秒懂性能优化💡
329 4
|
测试技术 持续交付 Apache
性能怪兽来袭!Python+JMeter+Locust,让你的应用性能飙升🦖
【10月更文挑战第2天】随着互联网应用规模的不断膨胀,性能测试变得至关重要。本文将介绍如何利用Python结合Apache JMeter和Locust构建高效且可定制的性能测试框架。Apache JMeter是一款广泛使用的开源负载测试工具,适合测试静态和动态资源;Locust则基于Python,通过编写简单的脚本模拟HTTP请求,更适合复杂的测试场景。
195 3