基于Docker的JMeter分布式压测实践:突破单机瓶颈 说起 JMeter 分布式压测很多人的第一反应是怎么把测试任务拆给多台机器一起跑。我最早折腾这个场景是因为一台 8C16G 的负载机在压力测试时 CPU 被打到 90% 以上TPS 曲线抖得像心电图。当时最现实的问题不是被测系统扛不扛得住而是压测工具这一侧先撑不住了。后来换成 Docker 编排的 JMeter 分布式集群才把负载真正摊开到多台执行机上也让压测环境的搭建从“装三台机器然后手工配环境”变成了“一条命令拉起 N 个节点”。这篇文章的核心就是把这个方案完整拆开为什么分布式压测需要容器化、JMeter 主从之间到底聊了些什么、Dockerfile 和编排文件怎么写得能落地以及跑完之后怎么判断数据有没有失真。适合的人群主要分两类一类是正在被单机压测瓶颈卡住的后端或测试工程师另一类是想把性能测试环境纳入流水线但又不想维护一堆裸机的团队。即便你没接触过 JMeter也可以按文中的步骤把环境搭起来先跑通再深挖原理。1. 分布式压测的思路与架构拆解1.1 单机压测撞上瓶颈的四个常见原因先聊为什么非得上分布式。很多刚入行的同学以为压测就是“开个 JMeter把线程数调到 10000然后看结果”。真这么干的话被压服务还没倒你的负载机往往先阵亡了。单机压测的瓶颈一般集中在四个地方CPU 资源JMeter 本身是 Java 应用高并发下会产生大量采样、断言、JSON 提取等操作一个线程组里几千个线程跑起来CPU 很容易被吃满。CPU 一旦饱和线程调度延迟会急剧上升你测出来的 TPS 根本不准。JVM 内存每个线程都有独立的运行栈和数据区线程数越多堆内存消耗越大。JVM 频繁 Full GC 时会出现“世界暂停”压测曲线会突然掉下去看起来像被测服务出问题其实是施压端自己卡了。文件描述符限制Linux 下每个连接都要占用一个 fd单机跑高并发时经常碰到 “Too many open files”。这个虽然可以通过调 ulimit 缓解但总归有上限。网络带宽与链接数施压机自身的网卡带宽、连接表大小在高并发下也是硬限制。特别是长连接类的压测单机连接数先撑不住后面业务只占了其中一小部分。这个道理和生活里点餐很像一个人再能干单子一多也会忙不过来饭馆排队久了出餐就是不稳定。与其指望一个超人厨师不如把单子分给几个窗口一起出餐。分布式压测解决的就是这个问题把原本由一个进程产生的并发拆到多台机器、多个 JMeter 进程上去让每台机器只承担总负载的一部分。1.2 JMeter 主从节点之间到底怎么协作JMeter 的分布式模型是经典的“Controller-Slave”结构。Controller 是调度机Slave 是从机官方文档里也把 Slave 叫 Agent。从机节点上跑的是一个叫jmeter-server的后台进程它在启动后会注册一个 RMI 端口默认是 1099等待调度机连过来。真正执行一次分布式压测的过程是这样的调度机把.jmx脚本文件通过 RMI 推送给所有从机。从机收到脚本后在自己的 JVM 里按脚本配置启动线程组产生并发请求。每个从机各自采集采样结果并回传给调度机。调度机汇总所有从机的采样结果保存到指定的.jtl或 CSV 文件里同时打印汇总日志。注意一个容易被忽视的点JMeter 分布式对脚本有两个隐藏要求。一是从机必须能访问到被测服务这个靠网络环境保证二是脚本里的 CSV 数据文件、外部 jar 包、依赖插件必须保证每一台从机上都有。后面我会专门讲 CSV 这个问题。主从之间的通信默认走 RMI并且在 JMeter 5.x 里默认开启了 SSL。所以在多台机器部署时常见的问题都集中在 RMI 端口不通、SSL 证书不一致、回调地址不对这三个坑上。Docker 方案把这些坑简化了不少但也不是完全没有后面第 5 章我会逐个排查。1.3 为什么在这个场景里选择 Docker可能有人会问我直接在几台裸机上装 JMeter 不行吗行但维护成本高。Docker 方案在压测这个场景里有一个很突出的价值你只需要维护一个镜像然后把同一份镜像按需复制出 N 个容器全部采用相同的 JVM 参数、相同的工作目录、相同的时区配置。裸机方案里最常见的问题就是“这台机器上 JMeter 是 5.4那台是 5.6”或者“A 机的 JDK 和 B 机不一样”跑出来的结果根本没有可比性。容器化之后这种环境漂移问题基本被抹平了。Docker 的另一个优势是弹性扩缩容。压力不够docker compose up -d --scale jmeter-slave5再拉几台起来就行。测完了直接down不占用常驻资源。裸机方案一般做不到这么轻量因为物理机总得有系统、有 JDK、有 JMeter闲置时就成了浪费。当然Docker 也有它自己的代价bridge 网络和端口映射会带来一点网络转发开销。做普通接口压测时这点开销可以忽略但如果是纯带宽型、PPS 非常高的压测建议改用 host 网络模式也就是让容器直接共享宿主机网络栈。方案本身没有绝对好关键看你的测试场景。2. 镜像构建与容器化环境准备2.1 基础镜像和 JMeter 版本选型的建议工欲善其事必先利其器。做镜像之前先把版本定清楚我的建议是JMeter 版本用你团队内部统一的版本目前 5.5 和 5.6 系列都比较成熟。不要一个集群里混搭版本很容易出现采样数据格式不一致的问题。基础镜像选 JRE 而不是 JDK因为压测节点只运行 JMeter不需要编译代码。JRE 镜像体积小很多构建和拉取都快。系统层建议优先选 Alpine 系镜像。JMeter 是纯 Java 程序不依赖 glibc 特性用 Alpine 能把镜像压到 200MB 左右。如果你对排查问题时的命令齐全度有要求也可以换 Ubuntu Slim大几十 MB 的体积差距通常可以接受。这里有件事需要特别说明JMeter 本身不内置任何第三方插件如果你在脚本里用了jpgc系列插件、redis数据集、kafka取样器等就必须在两个位置同步处理。一是镜像里提前装好对应插件二是插件版本必须一致。分布式模式下插件不一致的报错很隐蔽经常是“本地跑得好好的远程一跑就报类找不到”查起来非常头疼。2.2 Dockerfile 编写要点和避坑细节下面给一份经过实战的 Dockerfile我会逐段解释关键行而不是让你直接抄完就完事。# 基础镜像Java 11 的 JRE 版本 FROM eclipse-temurin:11-jre-alpine ENV LANGC.UTF-8 \ JMETER_VERSION5.6.3 \ JMETER_HOME/opt/jmeter # 安装运行时依赖注意是 apk 不是 apt RUN apk add --no-cache wget bash tzdata procps \ wget -q https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-${JMETER_VERSION}.tgz \ tar -xzf apache-jmeter-${JMETER_VERSION}.tgz -C /opt \ ln -s /opt/apache-jmeter-${JMETER_VERSION} ${JMETER_HOME} \ rm -f apache-jmeter-${JMETER_VERSION}.tgz # 设置时区避免生成报告时间差 8 小时 RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 创建普通用户避免容器一跑就是 root RUN adduser -S -s /bin/bash jmeter ENV PATH${JMETER_HOME}/bin:${PATH} \ JVM_ARGS-Xms512m -Xmx2g USER jmeter WORKDIR /jmeter CMD [jmeter-server]第一行的eclipse-temurin:11-jre-alpine是 Java 11 的 JRE 镜像JMeter 5.6 官方要求 Java 8 以上但生产环境我强烈建议用 11 以上因为新版本 JMeter 对高并发下的字节码处理和垃圾回收表现更好。apk add --no-cache wget bash tzdata procps这一段tzdata是时区数据procps提供ps命令。为什么要把ps装进去因为后续排查线程和内存问题时进容器里看一眼进程信息非常重要。很多精简镜像把ps省掉了排障时连ps -ef | grep java都敲不了非常痛苦。下载解压这一段需要注意一个实际问题archive.apache.org这个官方归档源在国内网络环境下速度不一定快。如果你构建时卡在这里可以采用两种兜底方式一是把压缩包先下载到本地然后在 Dockerfile 里改用COPY apache-jmeter-5.6.3.tgz /opt/再解压二是换成你团队内部搭建的软件镜像源。别硬等构建镜像卡在网络上下载上是最浪费时间的事。adduser这步可能有人觉得多余但实际很有必要。用 root 跑 JMeter 虽然当时没事一旦脚本里有“读文件失败”或者“权限异常”你没法确定是权限问题还是脚本逻辑问题。统一降权到普通用户环境更干净排查方向也更明确。2.3 构建镜像并做一次单容器冒烟验证Dockerfile 写完后先构建再验证镜像里面的 JMeter 能正常启动不要急着上编排。docker build -t jmeter:5.6.3 . docker run --rm jmeter:5.6.3 jmeter -v第二条命令如果能看到 JMeter 的版本信息说明基础环境是通的。还可以再跑一个更严格的检查docker run --rm jmeter:5.6.3 jmeter-server --helpJMeter 的jmeter-server支持--help参数如果它把 JMeter 版本和 Java 版本都打印出来说明 RMI 服务的主程序可以正常启动。到这一步镜像基本没问题了。3. 用 Compose 编排从机集群3.1 容器网络规划和 RMI 端口的关键点镜像有了接下来是网络规划。我这里强烈推荐使用 Docker 自定义 bridge 网络并且把调度机和从机放在同一个网络里。这样有一个巨大的好处容器之间可以直接用容器名互相访问比如从机容器名叫jmeter-slave1调度机里直接写jmeter-slave1:1099就能连上不用关心底层 IP。这里要理解 RMI 通信其实是两个阶段第一阶段是调度机连接从机的 RMI 注册端口默认 1099第二阶段是从机回传数据时会建立一个数据通道这个数据端口默认是随机分配的。跨公网或跨宿主机时随机端口很容易被防火墙拦死但放在同一个 Docker bridge 网络中所有容器间的端口都是互通的随机端口问题基本不存在。所以一种稳妥的策略是调度机和从机全部进同一个自定义网络从机端口不映射到宿主机。除非你非要让宿主机上的 JMeter 连接容器里的从机那才需要ports: - 1099:1099。我个人的经验是不要让宿主机上的 JMeter 直连容器从机那会把简单问题复杂化把调度机也容器化是最省心的。3.2 一份可直接落地的 Compose 配置文件下面给出一份示例配置。为了清晰地帮助理解我显式创建了两个从机节点注意这里用了container_name。固定容器名的好处是调度端配置remote_hosts时非常明确不容易连错。services: jmeter-controller: image: jmeter:5.6.3 container_name: jmeter-controller networks: - jmeter-net command: [sleep, infinity] volumes: - ./test:/test - ./result:/result - ./data:/data depends_on: - jmeter-slave1 - jmeter-slave2 jmeter-slave1: image: jmeter:5.6.3 container_name: jmeter-slave1 networks: - jmeter-net environment: - RMI_HOST_DEF-Djava.rmi.server.hostnamejmeter-slave1 volumes: - ./data:/data mem_limit: 2g cpus: 1.5 jmeter-slave2: image: jmeter:5.6.3 container_name: jmeter-slave2 networks: - jmeter-net environment: - RMI_HOST_DEF-Djava.rmi.server.hostnamejmeter-slave2 volumes: - ./data:/data mem_limit: 2g cpus: 1.5 networks: jmeter-net: driver: bridge调度机我用的是固定容器jmeter-controller启动后跑sleep infinity而不是直接执行压测因为docker compose exec进入容器执行命令要比docker compose run更灵活可以反复发压测任务不需要每次都重新拉容器。如果你喜欢“用完即走”的临时容器也可以不建固定 controller直接用docker compose run --rm jmeter-controller ...这取决于你的使用习惯。从机环境变量里有一个关键参数RMI_HOST_DEF-Djava.rmi.server.hostnamejmeter-slave1。这是很多人在容器环境下手动启动jmeter-server时常忽略的参数。JMeter 启动从机时会用它作为 RMI 服务注册地址如果该地址不对调度机即使连上了端口后续 RMI 回调也会失败。在 compose 里通过环境变量把容器名直接写进去是最不容易出错的方案。3.3 启动集群并检查节点状态按照上面的配置一键启动docker compose up -d docker compose ps正常情况下你会看到jmeter-controller、jmeter-slave1、jmeter-slave2三个容器都是 Up 状态。接下来进 controller 容器里验证一下网络和端口。docker exec -it jmeter-controller bash nc -vz jmeter-slave1 1099 nc -vz jmeter-slave2 1099nc -vz是测试 TCP 连通性的常用命令。如果输出里能看到Connected说明从机的 RMI 注册端口可以从调度机访问。如果容器里没有nc可以用docker exec jmeter-slave1 jmeter -v这种间接判断但不如端口探测直接。4. 调度机配置与分布式压测执行4.1 调度机的关键属性配置分布式执行可以完全不改 JMeter 的配置文件直接在命令行里用-R指定远程从机地址。但如果你希望“一键启动所有远程从机”那就需要把从机地址写进调度机的jmeter.properties。假设你的调度机环境变量已经挂载了配置目录在该目录下的jmeter.properties里加一行remote_hostsjmeter-slave1:1099,jmeter-slave2:1099还有一种不需要配置文件的方式启动时用参数覆盖jmeter -n -t /test/test.jmx -l /result/result.jtl -R jmeter-slave1,jmeter-slave2-R后面的内容是“远程主机列表”多个地址用英文逗号分隔默认端口 1099 不写也可以。如果从机改了端口比如第二台是 1100那就要写成jmeter-slave2:1100。这里还要关注一个 SSL 问题。JMeter 5.x 默认开启 RMI SSL初次分布式通信时调度机会尝试把证书信息同步给从机。如果你在一个可信的内网测试环境里不想被证书问题折腾可以统一在两侧关闭 SSL。具体做法是在jmeter.properties里增加server.rmi.ssl.disabletrue调度机和从机两侧都要配置一致。如果只在一侧改另一侧还是 SSL 模式就会出现各种奇怪的连接失败。当然如果你在跨安全域的环境里还是建议保留 SSL并按照官方文档配置密钥库不要贪图方便牺牲安全性。4.2 CSV 数据和全局属性的同步技巧我见过很多“本地脚本不报错远程执行一堆错”的情况七八成都是 CSV 数据文件的问题。JMeter 中的CSV Data Set Config是依赖本地文件系统的从机容器和调度机容器各自有独立的文件系统调度机上存在/data/users.csv不意味着从机上也有。解决方案其实不复杂重点是要统一规划Compose 里把./data目录挂载到所有从机和调度机的同一路径/data。CSV 配置里的文件名填绝对路径比如/data/users.csv避免使用相对路径。如果数据量很大不要把数据文件塞进镜像。容器启动时挂载体积即可既方便更新又不会撑大镜像。另一个要注意的是压测参数的传递。JMeter 脚本里经常用${__P(users)}这类属性占位符本地执行时可以用-Jusers500传入。但在分布式模式下如果你只写了-J这个参数只对调度机本地进程生效从机上是拿不到的。必须用-G来传递全局属性docker exec jmeter-controller jmeter -n -t /test/test.jmx -l /result/result.jtl \ -R jmeter-slave1,jmeter-slave2 \ -Gusers500 -Grampup30-G会把属性值随脚本一起推送到所有从机这样从机在执行${__P(users)}时才能拿到 500 这个数值。这是分布式压测中特别容易踩坑的地方宁可每次执行前都显式加-G也不要依赖默认值。4.3 发起一次完整的分布式压测先准备好测试脚本假设脚本文件放在宿主机./test目录下Compose 里已经挂载到/test。然后执行docker exec jmeter-controller jmeter -n \ -t /test/order_flow.jmx \ -l /result/result_$(date %Y%m%d_%H%M%S).jtl \ -R jmeter-slave1,jmeter-slave2 \ -Gusers500 -Grampup30 \ -Gduration300命令逐段解释-n是 CLI 非 GUI 模式-t指定脚本-l指定结果文件。-R指定远程从机。最后的三个-G把 500 线程、30 秒梯度递增、300 秒持续时长同步推送给两台从机。执行过程中调度机会持续打印类似这样的汇总日志summary 5627 in 00:00:05 1125.4/s Avg: 52 Min: 15 Max: 1200 Err: 0 (0.00%) summary 98772 in 00:00:30 3292.4/s Avg: 48 Min: 12 Max: 1080 Err: 0 (0.00%) summary 162320 in 00:05:00 3246.4/s Avg: 46 Min: 11 Max: 960 Err: 0 (0.00%)后面的数字是 5 秒或 30 秒窗口内的增量采样斜杠后面的xx/s是当前吞吐率。看到这个汇总能初步判断整体 TPS 和错误率。分布式模式下最终.jtl文件是由调度机汇总写出的所以后续用 JMeter 的聚合报告插件或自研报告解析脚本直接读这个文件即可。4.4 怎么确认每个从机真的“出了力”这一步容易被忽略。有时候你拉起两台从机但脚本写得不合理导致所有请求都堆在同一个节点上或者某台从机 JVM 内存不够线程根本没跑起来。这种情况看调度机的汇总数据看不出来必须到从机侧看。最简单的办法是看从机日志docker logs jmeter-slave1 docker logs jmeter-slave2每台从机都会打印自己接收到的任务信息和采样进度。如果某一台从机的日志里采样数量明显少于另一台就要查网络和资源限制。另外可以用docker stats实时观察资源占用docker stats jmeter-slave1 jmeter-slave2正常情况下两台从机的 CPU 和内存使用率应该比较接近。如果 A 机 CPU 80%B 机只有 20%大概率是脚本或数据分布有问题而不是“B 机偷懒”。5. 常见问题与排障实录5.1 RMI 连接失败报错 Could not connect to host这是分布式压测里出现频率最高的错误。日志里通常会给出类似Could not connect to host jmeter-slave1:1099的信息。排查顺序我建议按下面几步走先在调度机容器里确认网络通不通nc -vz jmeter-slave1 1099如果不通看 Compose 网络配置是不是同一个网络从机有没有正常启动。确认从机的RMI_HOST_DEF环境变量是否设置正确。很多人在手动docker run时漏了这个导致从机把 RMI 服务地址注册成容器 ID 或 0.0.0.0调度机连不上。检查两侧的 SSL 开关是否一致。一侧关了另一侧没关就会出现“连接被重置”之类的诡异错误。5.2 从机能连上但压测一会就连接被重置这种问题往往不是网络配置而是 JVM 内存和线程资源到了极限。从机线程数跑满后JVM 频繁 Full GC可能导致 RMI 心跳超时调度机认为从机失联主动断开连接。排查方法分两步先看docker stats里从机内存是不是一直在高位再看从机的jstack日志确认是否有明显的 Full GC。我遇到过最典型的情况是明明 Compose 里给了mem_limit: 2g但 JMeter 脚本里设置了 3000 线程每个线程栈再吃点堆外内存2g 根本不够。解决办法很简单要么降低单机线程数要么调大JVM_ARGS里的-Xmx。5.3 从机报 CSV 文件找不到或路径错误错误信息通常是Cannot open file: user.csv大部分原因是相对路径。JMeter 在从机上运行脚本时当前工作路径不一定是你本地运行时的路径相对路径就可能找不到文件。建议把所有数据文件都放到挂载目录下并配置绝对路径。另外要注意编码问题。如果你的 CSV 文件是 GBK 编码而 JMeter 默认按 UTF-8 读取就会出现中文乱码但程序不报错的情况。我会把 CSV 统一转成 UTF-8无 BOM格式这也是个容易忽略的细节。5.4 线程数翻倍但 TPS 没翻倍是不是分布式无效很多人以为分布式就是把线程数“平均分给几台机器”但 JMeter 的机制是每台从机都会完整执行脚本里的线程组配置。如果你在脚本里写死线程组为 1000那两台从机加起来就是 2000 并发不是每台 500。当你觉得“已经加了三台机器TPS 应该暴涨”却并没有时先检查两件事一是线程组参数是不是写死了导致三台从机都以相同线程数跑二是被测服务本身是不是已经到了瓶颈。分布式只解决施压端瓶颈不解决被测端的处理能力上限。如果被测服务已经饱和你把施压机加到十台TPS 也不会涨只会让失败率更高。5.5 宿主机端口冲突与管理心得如果多个从机容器都映射 1099 到宿主机那第二个容器就会起不来报端口占用。解决方案有两种从机不映射端口到宿主机只在自定义 bridge 网络内部通信。目前我推荐这种。如果必须从宿主机访问就分别映射不同端口比如10990:1099、10991:1099并且在remote_hosts里写成宿主机IP:10990。这里顺手分享一个心得不要在分布式压测里试图把所有容器都container_name写死然后用--scale扩缩容。Compose 的--scale和固定容器名是冲突的会报 “name already in use”。如果你的需求是“随时调整从机数量”建议去掉container_name写一段脚本通过docker inspect动态生成remote_hosts。如果需求是“固定两到三台从机跑测试”像我上面那样显式写清楚反而更稳定。6. 资源规划和后续扩展经验6.1 从机数量和单机线程数的估算方法分布式压测的容量规划核心公式很简单总并发 单从机线程数 × 从机数。但这个“单从机线程数”不是拍脑袋定的它受限于从机的 CPU 核数、堆内存和脚本复杂度。一个粗略的经验值在只做简单 HTTP 请求和断言的脚本下单从机大概可以支撑 5001500 并发线程。如果脚本里有复杂的 JSR223 脚本、数据库断言、正则表达式提取单机支撑的线程数会大幅下降这时宁可减少线程数、增加从机数。举个例子目标总并发 6000脚本简单计划用 6 台从机每台跑 1000 线程。那么每台从机至少需要 2 核 CPU、2GB 堆内存。在 Compose 里可以配置为cpus: 2和mem_limit: 2g但别忘了一点mem_limit只是容器内存上限JVM 的堆大小还要单独通过JVM_ARGS-Xmx2g控制。有时候容器给了 4gJVM 默认最大堆只有 1/4 可用内存也就是 1g线程一多直接 OOM。这属于“给了资源但 JVM 没吃到”的经典问题。6.2 把压测环境嵌入流水线的思路这套 Docker 方案的另一个价值是可以被 CI 系统调用。你只需要在流水线里做几件事构建镜像、启动 from 机集群、把脚本和数据传到挂载目录、执行docker exec jmeter-controller jmeter -n ...、最后收集结果文件并解析 TPS 和错误率。我在实际项目里是按这个脚本模式做的# 1. 拉起从机集群这里假设已经定义了 compose 文件 docker compose -f jmeter-compose.yaml up -d --wait # 2. 动态生成从机列表不带 container_name 时 REMOTE_HOSTS$(docker compose -f jmeter-compose.yaml ps -q jmeter-slave \ | xargs -I{} docker inspect -f {{.Name}} {} \ | tr \n , | sed s/,*$//) # 3. 执行压测 docker exec jmeter-controller jmeter -n \ -t /test/api_benchmark.jmx \ -l /result/api_benchmark.jtl \ -R $REMOTE_HOSTS \ -Gthreads300 -Gduration180 # 4. 收集结果并停止集群 docker compose -f jmeter-compose.yaml down这段脚本里的REMOTE_HOSTS是动态生成的解决了我前面提到的“固定容器名和 scale 冲突”问题。压测结束后立刻down释放资源整个环境从启动到销毁不超过十分钟。这个模式如果配合按需触发能省掉很大一笔常驻测试机成本。6.3 整个方案我印象最深的三件事回过头来总结一下虽然文章主体已经写完了但有几条心得还是要单独拎出来讲因为它们是我实际踩坑踩出来的不是看文档能看出来的。第一RMI 和 SSL 是两个最容易消磨耐心的点。只要你的网络环境复杂一点比如从机跨了宿主机、或者公司网络开了防火墙问题就会从“连接失败”变成“时好时坏”。我后来养成的习惯是先在同一个 Docker 网络里把所有节点拉通功能验证没问题以后再考虑跨宿主机的部署。不要一开始就挑战高难度拓扑。第二脚本里的相对路径、线程组写死参数、数据文件命名编码这三个问题只要犯一个分布式压测结果就不可信。你花了很多时间搭环境最后数据却因为这类低级问题作废非常不值。建议在跑正式压测之前先拿一个最简单的脚本做全链路冒烟确认每一台从机都在正常工作再换真实脚本上量。第三分布式压测不是在“测试一个更大的并发数”而是在“验证被测系统在多负载源条件下的真实表现”。所以从机分布越分散结果越接近真实流量分布。如果条件允许不妨把从机跨宿主机部署到不同物理机甚至不同机房这也是 Docker 方案相比“单台大机器多线程”的进阶价值。我自己最后再分享一个小技巧正式数据出来以后不要只看平均 TPS要把每个从机的采样单独拉出来分析。因为 JMeter 汇总结果时平均和百分位会掩盖单台机器的异常。一旦发现某台从机的响应时间显著偏高先查它的资源使用和网络连接而不是急着甩锅给被测系统。压测工具测出来的结果首先得保证工具自身没毛病这条原则无论用多少台机器、多少个容器都不会变。