【0 基础也能学会】JMeter 全方位指南:如何开始简单的 WEB 压力测试?
作者:ddos攻击压力测试【网址:kv69.com】
📖 前言:背景与初衷
🌟 为什么我们需要性能压测?
在最近的工作项目中,我接到了一个至关重要的任务:针对公司核心的 Web 网站进行性能压测。这个任务的目标非常明确,那就是在特定的硬件配置环境下,准确评估我们的 Web 网站究竟能够支持多少并发用户数。这不仅仅是一个数字游戏,它直接关系到公司在即将到来的大促活动中,系统是否会因为流量洪峰而崩溃,进而导致业务损失和用户流失。
想象一下,当成千上万的用户在同一时刻涌入网站,如果服务器无法承载,页面加载缓慢甚至直接报错,那将是灾难性的后果。因此,性能压测不再是“可选项”,而是上线前的“必选项”。
🛠️ 为什么选择 JMeter?
在接到任务后,我开始调研市面上流行的性能测试工具。LoadRunner 功能强大但昂贵且笨重;AB 工具简单但功能单一。最终,我将目光锁定在了 Apache JMeter 上。
JMeter 是一款开源的、基于 Java 的性能测试工具。它之所以成为行业主流,主要得益于以下几点:
- 完全免费开源:无需支付昂贵的授权费用,社区活跃,插件丰富。
- 跨平台支持:基于 Java 开发,可以在 Windows、Linux、Mac 等任何安装了 JDK 的系统上运行。
- 功能强大:不仅支持 HTTP/HTTPS Web 接口测试,还支持数据库、FTP、TCP、SOAP 等多种协议。
- 易于扩展:支持自定义函数和插件,能够适应复杂的业务场景。
考虑到 JMeter 是流行的 Web 性能压测工具,因此趁着这次机会,我上网查阅了大量的 JMeter 资料,也亲自动手进行了软件的配置和调测。从最基本的发送 HTTP 请求,到参数化输入,再到分布式测试和最后的实时结果展示,我都完整地“折腾”了一遍。
📝 本文的目的
现将整个折腾过程进行深度整理,既作为自己日后参考的知识库,也可分享给有需要的朋友,帮助大家少踩一些坑。在折腾的过程中,我主要参考了 JMeter 官网的详细手册和教程,同时也结合了许多技术博客和 StackOverflow 上的实战经验。
重新整理的过程,本身也是一个学习提高的过程。我复习了相关的性能测试概念,并找到了一些更易于理解的操作方法。接下来,我将按照作为一名初学者对性能压测的真实疑问,对整个过程进行系统性的梳理。
🗺️ 文章导航:我们将解决哪些问题?
为了让大家能够循序渐进地掌握 JMeter,我将内容划分为以下几个核心章节,每个章节都会辅以实际的例子进行解答,并将具体操作的步骤和逻辑详细记录下来:
- 🌱 如何开始简单的 WEB 压力测试 (一):发起第一个 HTTP 请求,打通任督二脉。
- 🔥 如何开始简单的 WEB 压力测试 (二):并发测试与参数化,模拟真实用户行为。
- 📹 如何录制测试计划:解放双手,自动捕获业务请求。
- 🖥️ 如何进行分布式压力测试:突破单机性能瓶颈,构建集群压测。
- 📊 如何实时展现测试结果:可视化监控,让数据说话。
- 🔍 深度解析:结果分析与调优:看懂报告,定位瓶颈。
- ⚠️ 避坑指南:常见问题与解决方案:前辈的血泪经验总结。
🏁 第一章:如何开始简单的 WEB 压力测试 (一) —— 发起第一个 HTTP 请求
俗话说万事开头难。对于初学者来说,面对 JMeter 复杂的界面,往往会感到无从下手。接下来,我们以一个最简单的例子,介绍如何从 JMeter 发起第一个 HTTP 请求。后续我们将在这个例子的基础上进行延伸,了解如何进行并发测试和参数化。
1️⃣ 安装 JMeter:工欲善其事,必先利其器
下载与版本选择 建议从 Apache 官网下载最新版本,以便获得更多新功能和安全性修复。虽然目前网络上流传着许多旧版本教程,但使用新版本可以避免很多已知的 Bug。JMeter 依赖 Java 环境,因此版本兼容性非常重要。目前主流版本支持 Java 8 及以上版本(JDK 1.8+)。
环境准备 在启动 JMeter 之前,请确保你的电脑已经正确安装了 Java Development Kit (JDK)。你可以在命令行中输入 java -version 来检查。如果未安装,需要先去 Oracle 或 OpenJDK 官网下载并配置好环境变量 JAVA_HOME。
目录结构解析 下载完成后,解压压缩包即可使用,无需复杂的安装过程。进入 JMeter 的 bin 目录,你会看到两个重要的启动文件:
jmeter.bat (Windows) 或 jmeter.sh (Linux/Mac):这是图形界面启动脚本,适合编写和调试脚本。jmeter-server.bat (Windows) 或 jmeter-server (Linux/Mac):这是分布式测试时的从节点启动脚本。
对于初学者,我们双击 jmeter.bat 即可启动图形界面。
2️⃣ 准备被测网站:寻找合适的靶场
在进行压测之前,我们需要一个目标网站。直接对公司生产环境进行压测是极其危险的,可能会导致线上故障。因此,我们需要一个安全的、可控制的测试目标。
推荐使用 PutsReq 被测网站建议使用 PutsReq 这样的在线测试服务。这个网站专门用于接受 HTTP 请求并且可以自定义返回结果,过程非常清晰,适合做为 HTTP 测试使用,且不会对真实业务造成影响。
操作步骤
- 访问 PutsReq 官方网站。
- 在网站首页上点击 "Create a PutsReq" 蓝色按钮。
- 网站会自动帮你创建一个唯一的 URL 路径。
- 接下来,你就可以往这个路径发送 HTTP 请求了。
- 你可以看到,如果是普通的 GET 请求,它会返回 "Hello World";如果是 POST 请求并带了 name 参数(假设 name 参数值为 XYZ),则会返回 "Hello XYZ"。
这个反馈机制非常直观,能够让我们立即确认 JMeter 发送的请求是否成功,以及参数是否正确传递。
3️⃣ 在 JMeter 中配置测试计划:构建测试骨架
启动 JMeter 后,你会看到一个空白的界面。JMeter 的测试计划(Test Plan)是所有测试元件的容器。我们需要按照层级关系添加元件。
第一步:添加线程组 (Thread Group) 首先在 Test Plan 中增加一个 Thread Group。在 JMeter 中,每个 Thread(线程)相当于一个模拟用户。在 Thread Group 中进行进一步的配置,可以定义模拟用户的行为,如发起各种请求、设置循环次数等。
- 操作路径:右键点击 Test Plan -> 添加 (Add) -> 线程 (Threads) -> 线程组 (Thread Group)。
- 参数说明:在本例中,Thread Group 创建后可以先不改参数,保持默认值(1 个线程,1 次循环)。
第二步:创建 HTTP 请求 (HTTP Request) 接着,我们需要告诉线程具体要做什么。这就需要一个采样器(Sampler)。
- 操作路径:右键点击 Thread Group -> 添加 (Add) -> 采样器 (Sampler) -> HTTP 请求 (HTTP Request)。
- 配置详解:
- 协议 (Protocol):填写
http 或 https。 - 服务器名称或 IP (Server Name or IP):填写 PutsReq 提供的域名。
- 端口号 (Port Number):通常 http 为 80,https 为 443,若默认可不填。
- 请求方法 (Method):选择
GET。 - 路径 (Path):填写 PutsReq 生成的具体路径。
- 让模拟用户发送 HTTP GET Request 到被测网站。
第三步:查看结果 (View Results Tree) 为了确认请求是否成功,我们需要一个监听器来查看响应数据。
- 操作路径:右键点击 Thread Group -> 添加 (Add) -> 监听器 (Listener) -> 查看结果树 (View Results Tree)。
- 作用:它可以显示每个请求的详细信息,包括请求头、请求体、响应头、响应体以及请求耗时。
4️⃣ JMeter 执行测试计划并查看结果
配置完成后,我们就可以通过 JMeter 执行刚刚配置好的测试计划。
执行操作 点击工具栏上的绿色“启动”按钮(或按 Ctrl + R)。JMeter 会根据线程组的配置,发送一个 HTTP GET Request 到被测网站 PutsReq。
结果验证 在 JMeter 上查看“查看结果树”中的请求结果。
- 如果图标显示为绿色盾牌,代表请求成功(状态码 200)。
- 点击该请求,在右侧的“响应数据”标签页中,应该能看到返回的一串字符 "Hello World"。
- 如果显示红色,则说明请求失败,需要检查网络、URL 或防火墙设置。
这一小节先整理到这里。我们成功迈出了第一步。下一小节我们将在这个例子的基础上进行延伸,介绍如何同时发起多个请求,并对请求的输入进行参数化,让测试更贴近真实场景。
🚀 第二章:如何开始简单的 WEB 压力测试 (二) —— 并发测试与参数化
在完成了单个请求的测试后,我们面临一个更现实的问题:真实的生产环境中,从来不是一个用户在访问网站,而是成百上千个用户同时操作。此外,每个用户输入的数据也是不同的。如果所有请求都一样,服务器可能会开启缓存,导致测试结果虚高。因此,我们需要引入并发和参数化。
1️⃣ 理解并发:线程组的深层配置
回到我们的 Thread Group(线程组),这里有三个核心参数决定了压测的模型:
- 线程数 (Number of Threads):代表模拟的用户数量。如果你设置为 100,就意味着 JMeter 会启动 100 个线程,每个线程独立执行测试任务。
- Ramp-Up 时间 (秒):这是一个非常关键的概念。它表示 JMeter 启动所有线程所需的时间。
- 举例:如果线程数是 100,Ramp-Up 时间是 10 秒。那么 JMeter 会在 10 秒内,每隔 0.1 秒启动一个线程。
- 意义:如果设置为 0,意味着 100 个线程会在同一毫秒内瞬间启动,这会对服务器造成瞬间的巨大冲击,类似于 DDoS 攻击,可能直接把服务打挂,且不符合用户逐渐进入系统的真实场景。通常建议根据服务器承受能力,设置一个合理的爬坡时间。
- 循环次数 (Loop Count):表示每个线程执行测试脚本的次数。
- 如果勾选“永远 (Infinite)",线程会一直运行,直到你手动点击停止。这适合进行稳定性测试(如持续运行 1 小时)。
- 如果填写具体数字,如 10,则每个用户执行 10 次请求后退出。
2️⃣ 参数化:让数据“活”起来
在注册、登录、搜索等场景中,每个用户提交的数据应该是不同的。如果 100 个线程都提交相同的用户名 user1,可能会导致数据库锁竞争,或者被服务器识别为异常流量。我们需要使用参数化技术。
方法一:CSV Data Set Config (推荐) 这是最常用的参数化方法,适合大量数据场景。
- 准备数据文件:在电脑上新建一个
data.csv 文件,用逗号分隔数据。例如:
- 添加配置元件:右键点击线程组 -> 添加 -> 配置元件 -> CSV Data Set Config。
- 配置参数:
- Filename:选择刚才创建的 csv 文件路径(建议使用绝对路径,避免相对路径问题)。
- Variable Names:填写变量名,如
username,password,对应 csv 中的两列。 - Recycle on EOF:是否循环读取。如果数据读完了,是否从头开始?压测时通常选 True。
- Stop thread on EOF:数据读完后是否停止线程。通常选 False,配合循环读取。
- 引用变量:在 HTTP 请求的参数值中,使用
${username} 和 ${password} 来引用刚才定义的变量。
方法二:随机函数 如果不需要特定数据,只是希望每次请求不同,可以使用 JMeter 内置函数。
- 在参数值处,点击函数助手对话框。
- 选择
Random 函数,生成随机数。 - 或者使用
RandomString 生成随机字符串。 - 这适合测试对数据唯一性要求不高的场景,如搜索关键词。
3️⃣ 添加断言:确保业务逻辑正确
压测不仅仅是看服务器有没有挂,还要看业务是否正确。如果服务器返回 500 错误,或者返回“系统繁忙”,虽然请求通了,但业务是失败的。我们需要添加断言 (Assertion)。
- 响应断言 (Response Assertion):
- 右键点击 HTTP 请求 -> 添加 -> 断言 -> 响应断言。
- 配置测试字段为“响应文本”。
- 配置模式匹配规则为“包括”。
- 填写期望的字符串,例如 "Hello" 或 "Success"。
- 如果响应中不包含该字符串,JMeter 会将该请求标记为失败。
- 作用:在最终的统计报告中,失败的交易会被单独统计,从而计算出真实的“业务成功率”,而不仅仅是“网络连通率”。
通过并发设置和参数化,我们的测试脚本已经从“玩具”变成了具备一定实战能力的“工具”。
📹 第三章:如何录制测试计划
手动编写每一个 HTTP 请求是非常繁琐的,尤其是面对复杂的业务链路(如:登录->搜索->加购->下单)。JMeter 提供了录制功能,可以像录音机一样记录下你在浏览器中的操作,并自动生成测试脚本。
1️⃣ HTTP(S) Test Script Recorder
这是 JMeter 内置的录制组件。
- 添加路径:右键点击 Workbench 或 Test Plan -> 添加 -> 非测试元件 -> HTTP(S) Test Script Recorder。
- 原理:JMeter 会启动一个本地代理服务器。你需要将浏览器的代理设置指向 JMeter 的代理端口(默认 8888)。当你在浏览器访问网站时,流量会经过 JMeter,JMeter 捕获请求并生成对应的 Sampler。
2️⃣ 证书安装:HTTPS 录制的拦路虎
现代网站大多使用 HTTPS 加密。如果直接录制,JMeter 无法解密流量,会导致录制失败或浏览器报错“连接不安全”。
- 生成证书:在 Recorder 组件界面,点击 "Create RCA Certificate" 按钮。JMeter 会在
bin 目录下生成一个 ApacheJMeterTemporaryRootCA.crt 文件。 - 导入证书:
- Firefox:在设置中搜索“证书”,查看证书,导入该文件,并信任该证书用于标识网站。
- Chrome:需要导入到操作系统的“受信任的根证书颁发机构”存储区。
- 注意:这一步是新手最容易卡住的地方,务必确保证书安装正确,否则浏览器会拦截请求。
3️⃣ 过滤与整理
录制过程中,会捕获到大量的静态资源请求(如 .css, .js, .png, .jpg)。这些资源会消耗 JMeter 的性能,且对接口逻辑测试无意义。
- 排除模式:在 Recorder 的 "Requests to Exclude" 列表中,添加正则表达式,如
.*\.(bmp|css|js|gif|ico|jpg|jpeg|png|svg|woff|woff2|ttf|eot|mp4|webm).*。 - 手动清理:录制完成后,脚本中可能包含多余的请求或硬编码的 Token。需要人工检查并清理,将动态数据(如 Session ID)替换为关联提取器(Correlation),这部分内容较为进阶,但录制能节省 80% 的建脚本时间。
🖥️ 第四章:如何进行分布式压力测试
当并发量达到一定程度(例如 1000 以上),单台 JMeter 机器可能会成为瓶颈。JMeter 自身运行需要消耗 CPU 和内存,如果压测机先挂了,测试结果就失去了意义。这时,我们需要分布式测试。
1️⃣ 架构原理:Master-Slave 模式
- 控制机 (Master):只有一台。负责发送测试指令,收集所有从节点的结果,并汇总报告。它不产生压力。
- 压力机 (Slave):有多台。负责实际执行测试脚本,产生负载。它们接收 Master 的指令,并将结果回传给 Master。
2️⃣ 配置步骤
第一步:配置从节点 (Slave)
- 在多台机器上安装相同版本的 JMeter 和 JDK。
- 进入
bin 目录,修改 jmeter.properties 文件。 - 找到
server_port,确保端口未被占用(默认 1099)。 - 找到
remote_hosts,这里通常不需要改,因为是从节点。 - 关键:确保从节点防火墙放行了 RMI 通信端口(1099 及随机端口)。
- 运行
jmeter-server 启动从节点服务。
第二步:配置主节点 (Master)
- 修改
jmeter.properties。 - 找到
remote_hosts 配置项。 - 填写所有从节点的 IP 地址,用逗号分隔。例如:
192.168.1.101,192.168.1.102。 - 确保主节点和从节点的网络互通,且时间同步(时间差异过大会影响结果统计)。
第三步:执行测试
- 在 Master 上打开脚本。
- 点击菜单栏 "运行 (Run)" -> "远程启动 (Remote Start)" -> "全部启动 (Remote Start All)"。
- 你会看到所有 Slave 机器开始运行,Master 界面实时显示汇总结果。
3️⃣ 注意事项
- 脚本同步:确保所有 Slave 机器上的测试脚本、CSV 数据文件、依赖的 Jar 包路径完全一致。建议使用相对路径,或将脚本打包分发。
- 网络带宽:Slave 向 Master 回传结果数据会占用网络带宽。在高并发下,建议关闭不必要的 Listener(如查看结果树),仅使用聚合报告,或使用 Backend Listener 将数据发送到外部数据库,减少网络开销。
- 安全设置:JMeter 的 RMI 通信默认是不加密的。在公网环境下进行分布式测试存在安全风险,建议在内网进行,或配置 SSL 加密。
📊 第五章:如何实时展现测试结果
测试执行完毕后,我们需要一份漂亮的报告来汇报工作。JMeter 提供了多种方式来展示结果。
1️⃣ 内置监听器
- 聚合报告 (Aggregate Report):最常用的报告。它提供了总样本数、平均值、中位数、90% 百分位、错误率、吞吐量等关键指标。
- 90% 百分位:表示 90% 的请求响应时间都小于这个值。它比平均值更能反映长尾延迟问题。
- 图形结果 (Graph Results):以折线图展示响应时间随时间的变化。可以直观看到性能波峰和波谷。
- 响应时间图 (Response Time Graph):更细致的响应时间分布。
⚠️ 警告:在高并发压测时,不要开启“查看结果树”。因为它会保存每个请求的详细信息,消耗大量内存,极易导致 JMeter 内存溢出(OOM)而崩溃。它仅用于调试脚本阶段。
2️⃣ 高级可视化:JMeter + InfluxDB + Grafana
为了达到“实时展现”且“高颜值”的效果,业界流行的方案是结合时序数据库和可视化大屏。
- 架构描述:
- JMeter:作为数据生产者。配置 "Backend Listener" 元件。
- InfluxDB:作为数据存储。它是一个高性能的时序数据库,专门用于存储带时间戳的指标数据。
- Grafana:作为数据展示。它连接 InfluxDB,提供丰富的图表模板,可以构建出极具科技感的监控大屏。
- 配置思路:
- 在 JMeter 中添加 Backend Listener。
- 选择
influxdbMetricsSender 实现类。 - 填写 InfluxDB 的 URL、数据库名、用户名密码。
- 启动 InfluxDB 服务和 Grafana 服务。
- 在 Grafana 中导入 JMeter 专用的 Dashboard 模板(社区有很多现成的 JSON 文件)。
- 执行测试时,Grafana 大屏上的曲线会实时跳动,TPS、响应时间、错误率一目了然。
这种方案不仅适合测试期间监控,也适合将测试历史数据保存下来,进行长期趋势分析。
🔍 第六章:深度解析 —— 结果分析与调优
拿到报告只是第一步,能够读懂报告并定位问题才是核心价值。
1️⃣ 关键指标解读
- TPS (Transactions Per Second):每秒事务数。这是衡量系统处理能力的核心指标。TPS 越高,系统性能越好。
- RT (Response Time):响应时间。包括网络传输时间、服务器处理时间、数据库查询时间等。
- Error Rate:错误率。在生产可接受范围内(通常要求 0% 或低于 0.1%)。
- 90th Line / 95th Line:90% 或 95% 的请求响应时间。如果平均值很小,但 90% 线很大,说明存在少量请求极慢,可能存在资源竞争或 GC 问题。
2️⃣ 瓶颈定位思路
当 TPS 上不去,或者 RT 变长时,我们需要分层排查:
- 客户端(JMeter)瓶颈:
- 检查 JMeter 所在机器的 CPU 和内存。如果 JMeter 机器 CPU 满载,说明压测机性能不足,需要增加 Slave 节点。
- 检查网络带宽是否打满。
- 网络瓶颈:
- 检查带宽利用率。
- 检查是否有防火墙限流或负载均衡器的连接数限制。
- 应用服务器瓶颈:
- CPU:是否使用率过高?是否存在死循环或复杂计算?
- 内存:是否频繁 Full GC?堆内存是否不足?
- 线程池:Web 容器(如 Tomcat)的最大线程数是否已满?导致请求在队列中等待。
- 数据库瓶颈:
- 慢查询:是否有 SQL 语句执行时间过长?
- 连接池:数据库连接数是否耗尽?
- 锁竞争:是否存在行锁、表锁导致等待?
通过结合操作系统监控工具(如 top, vmstat, iostat)和数据库监控,可以逐步缩小问题范围。
⚠️ 第七章:避坑指南 —— 常见问题与解决方案
在折腾 JMeter 的过程中,我遇到了不少问题,这里总结出来,希望能帮你节省时间。
❌ 问题 1:Java 内存溢出 (OutOfMemoryError)
- 现象:JMeter 运行一段时间后崩溃,日志提示 Heap Space 不足。
- 原因:默认 JVM 堆内存较小,无法支撑大量线程或大量数据结果。
- 解决:修改
jmeter.bat 或 jmeter.sh 中的 HEAP 参数。例如设置为 -Xms2g -Xmx2g,根据机器物理内存适当调大。同时,压测时务必禁用“查看结果树”。
❌ 问题 2:连接超时 (Connection Timeout)
- 现象:请求大量失败,报错 Timeout。
- 原因:服务器处理不过来,或者网络不稳定,或者 JMeter 设置的超时时间太短。
- 解决:
- 检查服务器负载。
- 在 HTTP 请求高级设置中,适当增加 "Connect Timeout" 和 "Response Timeout" 的值。
- 检查是否有防火墙拦截了高频请求。
❌ 问题 3:SSL 证书信任问题
- 现象:访问 HTTPS 接口报错
PKIX path building failed。 - 原因:JMeter 不信任目标网站的证书(特别是自签名证书)。
- 解决:
- 在
jmeter.properties 中设置 https.default.protocol=TLS。 - 或者在 HTTP 请求 sampler 中,勾选 "Use Truststore" 并指向正确的证书库。
- 测试环境下,可在代码层面忽略证书验证(不推荐生产环境)。
❌ 问题 4:分布式测试连接失败
- 现象:Master 无法连接 Slave,报错
Connection refused。 - 原因:防火墙拦截、IP 配置错误、JDK 版本不一致。
- 解决:
- 暂时关闭防火墙测试。
- 确保 Master 和 Slave 的 JMeter 版本、Java 版本完全一致。
- 检查
jmeter.properties 中的 server_port 是否一致。
🌈 第八章:最佳实践与建议
为了让你的压测工作更加专业和规范,以下是一些最佳实践建议:
- 环境隔离:严禁在生产环境直接进行全量压测。应在独立的测试环境进行,且测试环境的硬件配置最好与生产环境保持一致(或按比例折算)。
- 数据准备:压测前,数据库中应预置足够的基础数据。例如,压测查询接口前,数据库里要有千万级的数据量,否则查询速度会虚快。
- 逐步加压:不要一开始就设置 1000 并发。应采用“阶梯式”压测,从 10 -> 50 -> 100 -> 200,观察系统在不同压力下的表现,找到性能拐点。
- 预热:正式测试前,先运行几分钟,让 JVM 完成类加载和 JIT 编译,让缓存热起来,这样得到的数据更稳定。
- 脚本版本管理:将 JMeter 脚本(.jmx 文件)纳入 Git 版本控制,记录每次变更,方便回溯和协作。
- 关注资源监控:压测不仅仅是看 JMeter 的报告,必须同时监控服务器资源(CPU、内存、磁盘 IO、网络)。没有资源监控的压测是盲目的。
🎓 结语:持续学习与成长
通过这篇文章,我们完成了从 JMeter 安装、第一个请求、并发参数化、录制、分布式测试到结果分析和监控的全流程梳理。
性能测试是一门深奥的学问,JMeter 只是手中的武器。真正的挑战在于对系统架构的理解、对瓶颈的敏锐洞察以及对业务场景的精准模拟。
- 不要满足于工具的使用:多学习 Linux 命令、网络协议(TCP/IP)、数据库原理、JVM 调优等底层知识。
- 保持好奇心:JMeter 社区有很多插件(如 Custom Thread Groups, WebSocket Samplers),不断探索新功能。
- 分享与交流:像本文一样,将你的经验整理分享,教学相长,能帮助你更深刻地理解知识。
希望这份指南能成为你性能测试之路上的垫脚石。愿你在压测的道路上,少踩坑,多发现,构建出坚如磐石的系统!
END 绵薄之力 🙏