JMeter 实战指南:从入门到复杂场景并发测试

JMeter 实战指南:从入门到复杂场景并发测试

作为后端开发者,确保我们构建的应用在高并发场景下依然稳定高效是至关重要的。Apache JMeter 作为一款开源的、基于 Java 的压力测试工具,因其功能全面、使用便捷且跨平台(Linux/Windows/macOS),成为了我们进行接口测试与性能(压力)测试的首选工具之一。本文将引导您从 JMeter 的基础配置入手,逐步构建一个涉及登录、数据提取、多步骤依赖的复杂场景并发测试。

前提条件: 由于 JMeter 是 Java 开发的,请确保您的测试环境中已正确安装 JDK。

JMeter 下载: JMeter 官网

一、JMeter 核心组件概览

在深入实践之前,我们先简单了解一下 JMeter 的几个核心组件,这有助于我们理解后续的操作:

  • 测试计划 (Test Plan): 所有测试元素的顶层容器,描述了测试的整体流程和配置。
  • 线程组 (Thread Group): 定义虚拟用户(线程)数量、并发策略(Ramp-Up 时间)和循环次数。每个线程模拟一个真实用户。
  • 取样器 (Sampler): 实际发送请求的组件,如 HTTP 请求、FTP 请求等。
  • 逻辑控制器 (Logic Controller): 控制取样器的执行顺序和逻辑,如 If 控制器、循环控制器等。
  • 配置元件 (Config Element): 为取样器提供配置支持,如 HTTP 请求默认值、HTTP Cookie 管理器、CSV 数据文件设置等。
  • 断言 (Assertion): 验证服务器响应是否符合预期,如响应断言、JSON 断言等。
  • 监听器 (Listener): 收集并展示测试结果,如查看结果树、聚合报告等。
  • 前置处理器 (Pre Processor) / 后置处理器 (Post Processor): 在取样器发送请求前/后执行的操作,如参数提取、数据准备等(例如 JSON 提取器、BeanShell 后置处理器)。
  • 定时器 (Timer): 控制请求的发送时机,如固定定时器、高斯随机定时器、同步定时器等。

二、并发测试实战:多步骤考试预约接口

我们将以一个“考试预约”的场景为例,该场景通常包含以下步骤:

  1. 用户登录,获取认证 Token。
  2. 获取考试列表信息,可能需要 Token。
  3. 根据选择的考试,获取该考试的场次信息,可能需要 Token 和考试 ID。
  4. 用户选择场次进行预约,需要 Token、考试 ID 和场次 ID。

我们将模拟多用户并发执行此流程。

三、JMeter 测试计划配置详解

(一)创建测试计划与线程组

  1. 启动 JMeter,默认会有一个空的测试计划。

  2. 右键点击【测试计划】 -> 【添加】 -> 【线程(用户)】 -> 【线程组】。

    线程组核心参数配置说明:

    • 线程数(并发用户数): 模拟的并发用户数量。例如,200 表示模拟 200 个用户。
    • Ramp-Up 时间 (秒): 所有线程在多长时间内全部启动。例如,线程数 200,Ramp-Up 时间 10 秒,则表示 JMeter 会在 10 秒内逐步启动这 200 个线程,平均每秒启动 20 个。
    • 循环次数: 每个线程执行测试逻辑的次数。若勾选“永远”,则持续执行直到手动停止。总请求数(不考虑逻辑控制器)约等于:线程数 * 循环次数 * 每个循环内的请求数

    (本教程后续会在(十二)处详细配置线程组参数,此处先创建)

(二)配置通用 HTTP 设置

为了简化后续 HTTP 请求的配置,我们可以添加一些全局配置元件。

  1. 添加 HTTP 请求默认值 (HTTP Request Defaults)

    • 右键点击【线程组】 -> 【添加】 -> 【配置元件】 -> 【HTTP 请求默认值】。
    • 配置说明:
      • 协议: httphttps
      • 服务器名称或 IP: 被测服务器的地址,例如 api.example.com
      • 端口号: 服务器端口,例如 80443
      • 内容编码: 通常为 UTF-8
      • 如果所有或大部分接口都访问同一服务器,配置此项能极大简化后续 HTTP 请求的配置。

  2. 添加 HTTP 信息头管理器 (HTTP Header Manager)

    • 右键点击【线程组】 -> 【添加】 -> 【配置元件】 -> 【HTTP 信息头管理器】。
    • 配置说明:
      • 用于添加所有 HTTP 请求共有的请求头,如 Content-Type: application/json
      • 如果某些请求头的值是动态的(例如认证 Token),我们可以先定义一个通用的,后续再针对特定请求覆盖或添加。变量可以使用 ${变量名} 的形式。


    (上图示例中的 Authorization 头通常在登录后动态获取,此处仅为演示添加固定头的用法。在实际测试中,登录接口本身不需要 Authorization,而后续接口会动态使用登录后获取的 Token。)

(三)步骤1:实现登录接口请求

  1. 添加 HTTP 请求 - 登录接口

    • 右键点击【线程组】 -> 【添加】 -> 【取样器】 -> 【HTTP 请求】。将其命名为“登录请求”。
    • 配置“登录请求”:
      • 黄色区域: 如果已配置【HTTP 请求默认值】,且此接口与默认值一致(如服务器名、端口),则此处可留空。
      • 红色部分:
        • 方法: 选择请求方法,如 POST
        • 路径: 接口的路径,例如 /api/auth/login
        • 内容编码: 若与默认值不同,可单独指定。
      • 消息体数据 (Body Data) 或参数 (Parameters):
        • 根据接口要求,如果是 POST/PUT 等请求,通常在“消息体数据”中填写 JSON、XML 等格式的请求体。
        • 如果是 GETPOST 表单,可以在“参数”中添加键值对。
        • 我们将使用 CSV 文件参数化登录账号和密码,因此这里可以使用变量 ${username}${password}

  2. 参数化登录数据 - 添加 CSV 数据文件设置 (CSV Data Set Config)

    • 为了模拟不同用户登录,我们需要将账号密码参数化。
    • 右键点击【登录请求】(或【线程组】) -> 【添加】 -> 【配置元件】 -> 【CSV 数据文件设置】。
    • 配置 CSV 数据文件设置:
      • 文件名: CSV 文件的绝对或相对路径(相对于 .jmx 文件)。例如 users.csv
        • CSV 文件内容示例 (users.csv):
          1
          2
          3
          username,password
          user1,pass1
          user2,pass2
      • 文件编码: 根据 CSV 文件实际编码填写,常用 UTF-8
      • 变量名称: 对应 CSV 文件首行的列名,用逗号分隔。例如 username,password
      • 忽略首行 (Ignore first line): 如果 CSV 首行是标题,设为 True
      • 分隔符: 默认为逗号 ,
      • 是否允许带引号 (Allow quoted data?): 如果数据中包含引号,设为 True
      • 遇到文件结束符再次循环 (Recycle on EOF?): True 表示读取完文件后从头开始,False 表示停止。
      • 遇到文件结束符停止线程 (Stop thread on EOF?): True 表示读取完文件后该线程停止执行。
      • 线程共享模式 (Sharing mode):
        • All threads (所有线程): 所有线程共享同一个 CSV 文件,每个线程依次取下一行数据。
        • Current thread group (当前线程组): 仅当前线程组内的线程共享。
        • Current thread (当前线程): 每个线程独立打开和读取文件(较少使用)。
        • Edit: 自定义标识符,用于跨线程组共享。

  3. 验证登录结果 - 添加响应断言 (Response Assertion)

    • 右键点击【登录请求】 -> 【添加】 -> 【断言】 -> 【响应断言】。
    • 配置响应断言:
      • Apply to: 通常是 Main sample only
      • 测试字段 (Field to Test):
        • 响应文本 (Text Response): 服务器返回的完整响应体。
        • 响应代码 (Response Code): HTTP 状态码,如 200, 401。
        • 响应信息 (Response Message): HTTP 状态消息,如 “OK”, “Unauthorized”。
        • 响应头 (Response Headers): 检查响应头中的特定内容。
        • 注意:响应文本不等于响应信息。如下图,响应数据有值,但响应信息为空,若选【响应信息】断言可能失败。

      • 模式匹配规则 (Pattern Matching Rules):
        • 包括 (Contains): 响应字段包含指定的字符串或正则表达式。支持正则。
        • 匹配 (Matches): 响应字段完全匹配指定的正则表达式。
        • 相等 (Equals): 响应字段与指定字符串完全相等(大小写敏感)。
        • 字符串 (Substring): 响应字段包含指定的字符串。不支持正则,等效于“包括”不使用正则的情况。
        • 否 (Not): 反转断言结果。
        • 或者 (Or): 当有多个断言模式时,任一匹配即成功。
      • 要测试的模式 (Patterns to Test): 点击“添加”输入期望的值。例如,对于登录成功,响应文本中可能包含 "success":true"code":200
      • 自定义失败信息 (Custom failure message): 断言失败时显示的自定义消息。

  4. 提取动态数据 - 添加 JSON 提取器 (JSON Extractor)

    • 登录成功后,服务器通常会返回一个认证 Token,后续接口需要携带此 Token。我们需要从登录响应中提取它。
    • 右键点击【登录请求】 -> 【添加】 -> 【后置处理器】 -> 【JSON 提取器】。
    • 配置 JSON 提取器:
      • Apply to: 通常是 Main sample only
      • Names of created variables: 提取出的值要赋给的变量名,例如 authToken
      • JSONPath expressions: 用于定位 JSON 数据中目标值的 JSONPath 表达式。
        • JSONPath 语法简介:
          • $:根对象。
          • .:子操作符。
          • []:子操作符或数组索引。
          • *:通配符,匹配所有元素。
          • ..:递归下降,搜索所有子孙节点。
          • [?(expression)]:过滤表达式。
          • 例如,如果响应是 {"data": {"token": "xyz123"}, "code": 0},提取 token 的表达式可以是 $.data.token
          • (为了确定正确的 JSONPath,可以先添加一个【查看结果树】监听器,运行一次请求,观察响应数据结构。)

            (上图显示了响应结构,假设 token 在 data.tokenValue)
      • Match No. (0 for random):
        • 0:随机匹配一个(当表达式匹配到多个值时)。
        • n (正整数):匹配第 n 个。
        • -1:匹配所有,并将它们存储为 varName_1, varName_2, …, 同时创建 varName_matchNr 存储匹配总数。
        • 如果确定只匹配一个值,通常填 1
      • Default Values: 当 JSONPath 表达式未匹配到任何值时使用的默认值。例如 TOKEN_NOT_FOUND
      • Compute concatenation var (suffix _ALL): 如果勾选,并且 Match No. 为 -1,会将所有匹配到的值用逗号连接起来存入名为 N_ALL 的变量。


    (实际配置,假设 token 路径为 $.data.tokenValue)

    深入了解 JSONPath:

  5. (可选) 变量作用域提升 - 添加 BeanShell PostProcessor

    • JMeter 中,一个线程组内定义的变量(如通过 JSON 提取器创建的)默认在该线程组内可见。如果需要在不同线程组之间共享变量(较少见,通常推荐参数化或属性),或者想通过 __P 函数在测试计划级别使用,可以将其提升为 JMeter 属性。
    • 右键点击【登录请求】 -> 【添加】 -> 【后置处理器】 -> 【BeanShell PostProcessor】。
    • 配置 BeanShell PostProcessor:
      • 在 Script区域输入:
        1
        2
        3
        4
        5
        // 将名为 authToken 的 JMeter 变量转换为全局属性 authTokenProp
        props.put("authTokenProp", vars.get("authToken"));

        // 或者使用 JMeter 内置函数 (更推荐)
        // ${__setProperty(globalToken, ${authToken}, )}; // 注意有两个下划线
        在后续请求中,可以通过 ${__P(authTokenProp)}${__property(authTokenProp)} 来使用这个全局属性。
        对于本教程的单线程组场景,直接使用 ${authToken} 即可,无需提升。此处仅为演示该功能。


    (图中示例使用了 __setProperty 函数,token 是 JSON 提取器中定义的变量名,token (第一个参数) 是新设置的属性名。建议属性名和变量名有所区分以避免混淆,如 globalAuthToken。)

(四)步骤2:实现获取考试信息接口

  1. 添加 HTTP 请求 - 获取考试信息

    • 右键点击【线程组】 -> 【添加】 -> 【取样器】 -> 【HTTP 请求】。命名为“获取考试信息”。
    • 配置:
      • 方法: 例如 GET
      • 路径: 例如 /api/exams
      • 请求头: 如果此接口需要认证,需添加 Authorization 头。在此请求下右键添加【HTTP 信息头管理器】,并添加 Authorization 头,其值为 Bearer ${authToken} (假设 Token 类型是 Bearer,authToken 是上一步提取的变量)。
        • 如果已在线程组级别添加了通用的信息头管理器(如 Content-Type),这里添加的会与全局的合并或覆盖同名头。


    (上图中,绿色区域演示了如何在请求参数中直接使用提取的 Token,但更规范的做法是通过 HTTP 信息头管理器添加 Authorization Header。)

  2. 提取考试 ID - 添加 JSON 提取器

    • 假设此接口返回考试列表,我们需要提取其中一个考试的 ID (examId) 用于下一步。
    • 右键点击【获取考试信息】 -> 【添加】 -> 【后置处理器】 -> 【JSON 提取器】。
    • 配置:
      • Names of created variables: examId
      • JSONPath expressions: 例如 $.data[0].id (提取列表第一个考试的 ID)
      • Match No.: 1
      • Default Values: EXAM_ID_NOT_FOUND

(五)步骤3:实现获取场次信息接口

  1. 添加 HTTP 请求 - 获取场次信息

    • 右键点击【线程组】 -> 【添加】 -> 【取样器】 -> 【HTTP 请求】。命名为“获取场次信息”。
    • 配置:
      • 方法: 例如 GET
      • 路径: 可能是 /api/exams/${examId}/sessions (注意路径中使用了上一步提取的 examId 变量)。
      • 请求头: 同样,如果需要认证,添加 Authorization: Bearer ${authToken}

  2. 提取场次 ID - 添加 JSON 提取器

    • 从此接口响应中提取场次 ID (sessionId)。
    • 右键点击【获取场次信息】 -> 【添加】 -> 【后置处理器】 -> 【JSON 提取器】。
    • 配置:
      • Names of created variables: sessionId
      • JSONPath expressions: 例如 $.data.sessions[0].sessionId
      • Match No.: 1
      • Default Values: SESSION_ID_NOT_FOUND

(六)步骤4:实现考试预约接口(关键并发点)

这是我们重点关注并发性能的接口。

  1. 添加 HTTP 请求 - 考试预约

    • 右键点击【线程组】 -> 【添加】 -> 【取样器】 -> 【HTTP 请求】。命名为“考试预约”。
    • 配置:
      • 方法: 例如 POST
      • 路径: 例如 /api/reservations
      • 消息体数据:
        1
        2
        3
        4
        5
        {
        "examId": "${examId}",
        "sessionId": "${sessionId}",
        "userId": "${username}" // 假设预约也需要用户信息, username 来自 CSV
        }
      • 请求头: 此请求通常需要特定的 Content-Type (如 application/json) 和 Authorization。可以在此 HTTP 请求下右键添加一个【HTTP 信息头管理器】进行配置。

  2. 实现精确并发 - 添加同步定时器 (Synchronizing Timer)

    • 为了模拟大量用户在同一时刻(或极短时间内)同时发起“考试预约”请求,我们需要使用同步定时器。它会阻塞线程,直到达到设定的线程数量后,再同时释放这些线程。
    • 右键点击【考试预约】HTTP 请求 -> 【添加】 -> 【定时器】 -> 【Synchronizing Timer】。
    • 配置同步定时器:
      • 模拟用户组的数量 (Number of Simulated Users to Group by): 每次集结多少个线程后同时释放。
        • 如果设为 0,则等同于线程组中定义的总线程数。
        • 如果设为 10,则 JMeter 会等待,直到有 10 个线程到达此步骤,然后同时释放它们。此值不能超过线程组的总线程数。
      • 超时时间以毫秒为单位 (Timeout in milliseconds):
        • 如果设为 0,Timer 将一直等待,直到线程数达到“模拟用户组的数量”。
        • 如果大于 0,例如 5000 (5秒),若 5 秒内未集齐指定数量的线程,Timer 将不再等待,直接释放已到达的线程。

    • 重要提示: 同步定时器应仅添加在需要精确并发控制的请求上,而不是所有请求。

(七)添加监听器以查看和分析结果

  1. 查看结果树 (View Results Tree)

    • 用于调试和查看单个请求的详细信息(请求、响应数据、响应头等)。在高并发测试时,建议禁用或仅在调试少量用户时启用,因为它非常耗费资源。
    • 右键点击【线程组】 -> 【添加】 -> 【监听器】 -> 【查看结果树】。
    • (之前在讲解 JSON 提取器时已提及,用于观察响应结构。)
  2. 聚合报告 (Aggregate Report)

    • 提供关键性能指标的汇总统计,是性能分析的主要依据。
    • 右键点击【线程组】 -> 【添加】 -> 【监听器】 -> 【聚合报告】。
    • 聚合报告参数解读:
      • Label: JMeter 元件的名称(如 HTTP 请求的名称)。
      • # Samples (样本数): 发出的总请求数量。
      • Average (平均值): 平均响应时间 (毫秒)。
      • Median (中位数): 50% 用户的响应时间小于此值。
      • 90% Line (90% 百分位): 90% 用户的响应时间小于此值。
      • 95% Line (95% 百分位): 95% 用户的响应时间小于此值。
      • 99% Line (99% 百分位): 99% 用户的响应时间小于此值。
      • Min (最小值): 最小响应时间。
      • Max (最大值): 最大响应时间。
      • Error % (异常%): 错误请求的百分比。出现错误时需结合服务端日志排查。
      • Throughput (吞吐量): 通常指 TPS (Transactions Per Second),即服务器每秒处理的请求数。越高代表服务器处理能力越强。
      • Received KB/sec (接收): 每秒从服务器接收的数据量 (KB)。
      • Sent KB/sec (发送): 每秒向服务器发送的数据量 (KB)。

  3. (可选) 用表格查看结果 (View Results in Table)

    • 以表格形式展示每个请求的结果,比聚合报告更细致,但同样消耗资源,高并发时慎用。

(八)最终配置线程组参数

回到我们之前创建的【线程组】,现在根据测试目标配置并发参数。

  • 参考本教程(一)中对线程组参数的解释。
  • 示例配置:
    • 线程数: 200
    • Ramp-Up 时间: 10 (秒) -> 每秒启动20个用户
    • 循环次数: 10 -> 每个用户执行10次完整流程

四、执行测试并生成 HTML 报告

  1. 保存测试计划: 点击菜单栏 文件 -> 保存测试计划为… (例如 exam_booking_test.jmx)。

  2. 运行测试: 点击工具栏的绿色启动按钮。

  3. 在聚合报告中配置结果文件(用于生成 HTML 报告):

    • 在【聚合报告】的“文件名 (Filename)”字段,浏览并指定一个路径和文件名,用于保存原始结果数据,后缀为 .jtl.csv。例如 D:\JMeter_Reports\exam_test_results.jtl
    • 重要: 最好在执行测试前配置好此项,这样 JMeter 会在运行时自动将数据写入文件。如果测试已运行完毕,也可以手动点击聚合报告中的“保存表格数据”按钮。

  4. 生成 HTML 报告 (推荐在 GUI 模式测试完成后或非 GUI 模式下使用):

    • JMeter 提供了一个非常美观和详细的 HTML 动态报告。

    • 方式一:通过 JMeter GUI 生成

      1. 测试运行完毕后,点击菜单栏【工具 (Tools)】 -> 【Generate HTML report】。
      2. 在弹出的对话框中:
        • Results file (CSV or JTL): 选择上一步保存的 .jtl.csv 文件。
        • user.properties file: 指向 JMeter bin 目录下的 user.properties 文件。
        • Output directory: 指定一个的文件夹用于存放生成的 HTML 报告。
      3. 点击【Generate report】按钮。成功后会提示。
      4. 打开指定的输出目录,找到 index.html 并用浏览器打开即可查看报告。
    • 方式二:通过命令行生成 (更推荐用于自动化和大规模测试)

      1
      2
      # 切换到 JMeter 的 bin 目录执行
      jmeter -g /path/to/your/results.jtl -o /path/to/your/empty_html_report_folder
      • -g: 指定 JTL 结果文件。
      • -o: 指定 HTML 报告输出的空文件夹。

五、JMeter 性能测试最佳实践与技巧

  1. 使用非 GUI 模式进行压力测试: JMeter GUI 模式消耗较多资源,仅适用于脚本开发和调试。进行正式的压力测试时,务必使用命令行(非 GUI)模式:

    1
    jmeter -n -t /path/to/your/test_plan.jmx -l /path/to/your/results.jtl -e -o /path/to/your/empty_html_report_folder
    • -n: 非 GUI 模式
    • -t: 指定 JMX 测试计划文件
    • -l: 指定 JTL 结果日志文件
    • -e: 测试结束后生成 HTML 报告
    • -o: HTML 报告输出目录 (必须为空)
  2. 合理命名元件: 为你的测试计划、线程组、取样器等元件取有意义的名称,方便调试和结果分析。

  3. 参数化数据: 使用 CSV Data Set Config 等方式参数化测试数据,使测试更接近真实场景。

  4. 使用断言: 确保你的请求得到了预期的响应,而不仅仅是 HTTP 200。

  5. 谨慎使用监听器: 【查看结果树】、【用表格查看结果】等详细监听器在GUI模式下非常耗费内存和 CPU。在正式压测时,应禁用它们,仅保留【聚合报告】或【Summary Report】并将结果写入文件。

  6. 思考时间 (Think Time): 使用定时器(如高斯随机定时器、固定吞吐量定时器)模拟用户的自然停顿,使测试更真实。同步定时器是特定场景下的并发控制。

  7. 关联 (Correlation): 熟练使用后置处理器(如 JSON 提取器、正则表达式提取器)提取动态数据,用于后续请求。

  8. 模块化与复用: 对于公共的测试片段(如登录流程),可以使用【测试片段 (Test Fragment)】和【模块控制器 (Module Controller)】或【Include Controller】来实现复用。

  9. 逐步增加负载: 不要一开始就用最大并发数,应逐步增加负载,观察系统瓶颈。

  10. 分布式测试: 当单台测试机无法产生足够负载时,可使用 JMeter 的分布式测试功能(Master-Slave 模式)。

六、总结

通过本文的介绍,您应该对如何使用 JMeter 进行一个涉及多步骤、数据依赖的 API 并发测试有了清晰的认识。从基础的线程组、HTTP 请求配置,到参数化、动态数据提取、响应断言,再到关键的同步定时器应用和专业的聚合报告解读、HTML 报告生成,这些都是构建有效性能测试场景的核心要素。

JMeter 的功能远不止于此,希望本文能为您打开一扇门,鼓励您在实际工作中不断探索和应用,以保障您所开发的 Web 应用程序的高效与可扩展性。作为后端开发人员,掌握 JMeter 这类工具,对于验证系统性能、发现瓶颈、推动产品质量提升具有不可替代的作用。祝您测试愉快!


JMeter 实战指南:从入门到复杂场景并发测试

https://lbs.wiki/pages/6815e9a4/

作者

李博帅

发布于

2024-05-17

更新于

2025-06-05

许可协议