负载测试工具

组织使用 负载测试 来预测其系统应对各种压力的能力。这是构建和维护优质用户体验的关键部分,只有拥有合适的工具才能实现这一点。

但市场上有如此多的负载测试工具,很难知道为您的下一个项目选择哪一个。在这里,我们将尝试简化决策过程。虽然我们不会挑选赢家或输家(详情如下),但我们将为您提供所需的背景信息,以便您自信地评估哪个工具能满足您的需求。最后,我们将通过一个示例来说明构建和维护分布式应用的转变如何增加了现代负载测试的复杂性。

哪个是最佳负载测试工具?

最好的负载测试工具是那些最适合您特定需求的工具。也就是说,选择总是主观的。基础设施和应用的需求差异巨大,您的负载测试重点将影响您的决策。

通常,团队使用负载测试来查看系统如何响应正常和峰值使用,包括如何处理不断变化的用户数量、事务和数据量。但不同团队有不同的工作流程偏好和技能集,因此应寻找在内部最有可能被采纳的负载测试工具。例如,一个独立的测试团队与在整个软件生命周期过程中包含测试的开发团队有着不同的优先级和视角。记住,一个工具可能被认为是同类最佳,但如果没有人使用它,也就没什么作用了。

A screenshot of annotated k6 results analysis in a Grafana dashboard.
Grafana 仪表盘显示负载增加时各种性能影响

由于这些不同的需求,不同的负载测试工具被设计出来以解决不同的用例。有些工具是免费的。有些提供高级支持。有些面向 QA/测试团队。有些则针对开发人员或 SRE。您需要决定哪种方法最适合您的设置,但如果您正在使用更现代化、以开发人员为中心的系统和团队,则应关注以下几个关键能力:

  • 易于使用的 API 和 CLI。选择直观、灵活且强大的工具,它们提供开发者熟悉的界面。
  • 支持熟悉的脚本语言。通过支持团队使用流行的语言(如 Javascript)编写脚本,从而更轻松地构建真实的测试。这样,您还可以复用模块和库,以便更好地构建和维护您的测试套件。
  • 自动化测试。通过将性能测试与您的 CI/CD 工具集成,您可以设置通过/失败标准,轻松查看是否达到了可靠性目标。然后,这些数据可用于跟踪服务水平目标 (SLO)。
  • 高性能。如果您想查看您的应用如何应对巨大的流量峰值,负载测试工具需要强大的引擎。您应该专注于您的系统如何处理负载,而不是您的工具生成负载的能力如何。
  • 可扩展性。寻找可用于构建和调试测试的负载测试工具,无论您是在本地机器、分布式集群还是云端运行测试。
  • 支持您偏好的数据存储。您能否将负载测试工具的指标发送到您偏好的后端进行存储和进一步分析?如果不同团队使用不同的后端怎么办?请务必确认您在这方面具有必要的灵活性。
  • 可扩展性。针对流行的基础设施元素(如 Kubernetes、SQL 和 MQTT)的插件可以极大地扩展负载测试工具的潜在用例。

通过关注这些因素,您将能够更好地识别性能问题、确保可靠性、优化性能、提供出色的用户体验并满足 SLO。

负载测试工具:开源 

在 Grafana Labs,开源是我们一切工作的核心,因此我们认为使用开源工具进行负载测试有很多好处。首先,它更具成本效益,因为无需担心任何许可或支持费用。入门也容易得多——只需安装软件即可开始测试。

此外,许多开源工具可以将结果发送到后端,以便您稍后使用第三方分析工具创建可视化。如果您不喜欢该工具,随时可以放弃它,而且无需担心如何取回您的数据。

An architectural diagram shows k6 pointing to a terminal. On a separate line, k6 and a visualization layer point to storage.
基本负载测试解决方案的架构,此处使用 Grafana k6 OSS。

就用例而言,开源负载测试工具是构建概念验证的一种极好的、经济高效的方式。在此基础上,您可以整合超出基本负载测试范围的功能。开源工具对于进行简单、不频繁测试的小型团队也很适用。

由于对开源替代方案的需求,有多个项目持续激发社区兴趣。以下是 10 个最受欢迎的开源负载测试工具,按字母顺序列出:

  1. ApacheBench
  2. Apache JMeter
  3. Artillery
  4. Gatling
  5. Grafana k6
  6. Locust
  7. Siege
  8. Taurus
  9. The Grinder
  10. Tsung

JMeter 是负载测试工具吗? 

Apache JMeter 是最受欢迎的负载测试工具之一,并且已经流行了一段时间。该开源项目于 1998 年首次发布,因其是昂贵、专有工具的免费替代品而引起关注,这些专有工具此前在市场上占据主导地位。

这个 Java 应用模拟不同类型的负载,这样您就可以看到对系统性能的潜在影响。测试人员可以使用代码执行脚本,但 JMeter 中的大多数脚本编写都在 UI 中完成。

JMeter 在许多方面表现出色。它是 GUI 驱动的,对于习惯无代码界面的人来说,通常不那么令人生畏,也更容易上手。这种设计也使其非常适合作为 LoadRunner 和 NeoLoad 等商业工具的替代品。此外,JMeter 原生支持多种不同的协议,包括 HTTP、REST API、FTP、LDAP、TCP,并提供了一个运行分布式负载测试的框架。

但正如我们已经讨论过的,不同团队有不同的需求。例如,同为开源的 Grafana k6 可能对那些寻求简单轻量级工具的人更具吸引力。k6 也更适合由多个角色进行测试的协作式跨职能工程团队,以及希望将负载测试集成到其开发工作流或 CI/CD 流水线中的团队。

负载测试工具:免费 vs. 商业

尽管开源负载测试工具有许多好处,但它并非适用于所有人。例如,当团队需要以下情况时,应考虑功能强大的解决方案:

  • 运行大型测试或将测试执行分发到多台机器上。
  • 进行持续性能测试。
  • 允许多个角色参与测试。
  • 跟踪和报告应用性能及长期可靠性。
  • 提高解决方案的可靠性和可用性。

在达到一定规模时,维护和扩展负载测试所需的投入会超过其带来的好处。如果您没有运行和大规模维护自有测试工具所需的资源,可以考虑与商业供应商合作,以便您可以专注于您的核心业务。

当然,这并非非此即彼的选择。许多组织结合使用开源和商业工具来满足其负载测试需求。无论如何,在将任何工具作为负载测试策略的支柱之前,请务必计算各种选项的总拥有成本。

并且务必确定将使用该工具的团队。例如,QA 测试人员、开发人员、站点可靠性工程师 (SRE) 以及其他人员可能会在贵组织的负载测试中发挥作用。

An architectural diagram shows partners, community, IT managers, app developers, QA engineers, and SREs connected to k6 Cloud.
随着更多角色开始参与测试过程,负载测试解决方案也需要随之发展。

以下是 10 个最受欢迎的付费负载测试解决方案,按字母顺序列出:

  1. Akamai CloudTest
  2. BlazeMeter
  3. Grafana Cloud k6
  4. Headspin
  5. LoadRunner
  6. LoadNinja
  7. LoadView
  8. NeoLoad
  9. New Relic
  10. Radview WebLOAD

API 负载测试工具:测试示例 

API 已成为现代软件的基础,使不同计算机程序之间能够通信。但由于它们被多种服务使用,这些 API 的负载可能是可变的且难以预测。让我们来看看 API 的负载测试,以及这如何反映了当今系统负载测试所涉及的更广泛的益处和挑战。

首先,让我们强调 API 负载测试的好处:进行 API 负载测试的团队可以缩短加载时间并提高整体性能。他们还可以通过避免中断和提高运营效率来降低失败风险并帮助降低成本。甚至有一些专为 API 负载测试设计的工具,包括 BlazeMeter、Grafana k6、JMeter、Postman 和 Taurus 等等。

通常,API 负载测试始于评估小型、独立的组件。随着每一次迭代,测试范围逐渐扩大,直到获得对 API 工作流的更完整、端到端的视角,并可能了解它如何与其他 API 交互。这包括增加请求数量、延长持续时间,并扩展测试的整体范围,直到您认为自己对 API 如何应对各种压力有了全面的了解。

设计 API 测试时,应考虑以下因素:

  • 您想测试的流程或组件
  • 您计划如何运行测试
  • 可接受性能的标准

确定这些后,API 测试策略通常遵循以下步骤:

  1. 编写测试脚本。编写用户流程,参数化测试数据,并对 URL 进行分组。
  2. 断言性能和正确性。使用检查来断言系统响应,并使用阈值确保系统性能在您的 SLO 范围内。
  3. 模拟并生成负载。选择合适的执行器,正确模拟符合您测试目标的负载。确保负载生成器位于应有的位置。
  4. 迭代您的测试套件。随着时间的推移,您将能够复用脚本逻辑(例如,用户登录流程或吞吐量配置)。您还可以运行范围更广的测试,或将其作为自动化测试套件的一部分。

接下来,我们将使用 k6 来详细解释和示例此过程中的步骤。如果您之前编写过测试脚本,k6 脚本应该会很熟悉。k6 测试使用 JavaScript 编写,k6 API 设计与其他测试框架类似。

识别测试组件

如前所述,您需要从小处着手,因此首先我们将使用以下脚本测试单个端点,该脚本使用了 k6 HTTP 模块

JavaScript
import http from 'k6/http';

export default function () {
  const payload = JSON.stringify({
    name: 'lorem',
    surname: 'ipsum',
  });
  const headers = { 'Content-Type': 'application/json' };
  http.post('https://httpbin.test.k6.io/post', payload, { headers });
}

从这里,您可以逐步测试更复杂、更完整的工作流程,从独立的 API 到集成的 API,再到端到端的 API 流程。

确定测试目的

在您了解要测试的流量模式之前,请勿配置负载测试。您是想验证在典型流量下的可靠性,还是想发现峰值流量下的问题和系统限制?答案将有助于确定您应该运行的负载测试类型。例如,您可以使用冒烟测试来验证系统在最小负载下的功能,而使用稳定性测试来确定系统在长时间运行负载时的性能下降情况。

模拟工作负载

在 k6 中,您可以通过以下方式模拟负载:

  1. 虚拟用户 (VUs),用于模拟并发用户
  2. 每秒请求数,用于模拟原始、真实世界的吞吐量

您可以在测试脚本中定义这些选项。在以下测试中,50 个并发用户持续运行默认流程 30 秒。

JavaScript
import http from 'k6/http';

export const options = {
  vus: 50,
  duration: '30s',
};

export default function () {
  const payload = JSON.stringify({
    name: 'lorem',
    surname: 'ipsum',
  });
  const headers = { 'Content-Type': 'application/json' };
  http.post('https://httpbin.test.k6.io/post', payload, { headers });
}

如果您想分析 API 端点性能,负载通常按请求速率报告——每秒或每分钟。

要根据目标请求速率配置工作负载,请使用 恒定到达率执行器,该执行器设置执行脚本函数的恒定迭代速率。每次迭代可以生成一个或多个请求。

要达到目标请求速率 (RequestsRate)

  1. 设置速率频率
  2. 获取每次迭代的请求数 (RequestsPerIteration)
  3. 将迭代率设置为每秒请求数除以每次迭代的请求数。
    rate = RequestsRate ÷ RequestsPerIteration

使用前面的示例达到 50 reqs/s 的目标

  1. timeUnit 选项设置为 1s。

  2. 每次迭代的请求数是 1。

  3. rate 选项设置为 50/1(即等于 50)。

    JavaScript
    import http from 'k6/http';
    
    export const options = {
      scenarios: {
        my_scenario1: {
          executor: 'constant-arrival-rate',
          duration: '30s', // total duration
          preAllocatedVUs: 50, // to allocate runtime resources     preAll
    
          rate: 50, // number of constant iterations given `timeUnit`
          timeUnit: '1s',
        },
      },
    };
    
    export default function () {
      const payload = JSON.stringify({
        name: 'lorem',
        surname: 'ipsum',
      });
      const headers = { 'Content-Type': 'application/json' };
      http.post('https://httpbin.test.k6.io/post', payload, { headers });
    }

    此测试在 http_reqs 指标上输出 HTTP 请求总数和 RPS

    # the reported value is close to the 50 RPS target
     http_reqs......................: 1501   49.84156/s
    
    # the iteration rate is the same as rps, because each iteration runs only one request
    iterations.....................: 1501   49.84156/s

    验证功能

    延迟和可用性通常是性能测试的两个关键指标。http_req_duration 指标报告延迟,http_req_failed 报告 HTTP 请求的错误率。之前的测试运行提供了以下结果:

    http_req_duration..............: avg=106.14ms min=102.54ms med=104.66ms max=198.93ms p(90)=113.78ms p(95)=114.58ms
        { expected_response:true }...: avg=106.14ms min=102.54ms med=104.66ms max=198.93ms p(90)=113.78ms p(95)=114.58ms
    http_req_failed................: 0.00% ✓ 0    ✗ 1501

    您可能还需要验证功能并报告错误。例如,某些应用故障只在特定负载类型下发生,例如高流量。这些错误很难发现,但如果您对 API 进行插桩并验证请求是否获得预期响应,就能找到它们。

    在 k6 中,检查 在测试执行期间验证条件,因此您可以使用它们来确认预期的 API 响应,例如 HTTP 状态或任何返回的数据。

    我们的脚本现在验证 HTTP 响应状态、头部和载荷。

    JavaScript
    import { check } from 'k6';
    import http from 'k6/http';
    
    export const options = {
      scenarios: {
        my_scenario1: {
          executor: 'constant-arrival-rate',
          duration: '30s', // total duration
          preAllocatedVUs: 50, // to allocate runtime resources
    
          rate: 50, // number of constant iterations given `timeUnit`
          timeUnit: '1s',
        },
      },
    };
    
    export default function () {
      const payload = JSON.stringify({
        name: 'lorem',
        surname: 'ipsum',
      });
      const headers = { 'Content-Type': 'application/json' };
      const res = http.post('https://httpbin.test.k6.io/post', payload, { headers });
    
      check(res, {
        'Post status is 200': (r) => res.status === 200,
        'Post Content-Type header': (r) => res.headers['Content-Type'] === 'application/json',
        'Post response name': (r) => res.status === 200 && res.json().json.name === 'lorem',
      });
    }

    在此片段中,所有检查都成功了

    my_scenario1 ✓ [======================================] 00/50 VUs  30s  50.00 iters/s
         ✓ Post status is 200
         ✓ Post Content-Type header
         ✓ Post response name

    负载增加到每秒 300 个请求后,结果返回了 8811 个成功请求和 7 个失败请求

    my_scenario1 ✓ [======================================] 000/300 VUs  30s  300.00 iters/s
         ✗ Post status is 200
          ↳  99% — ✓ 8811 / ✗ 7
         ✗ Post Content-Type header
          ↳  99% — ✓ 8811 / ✗ 7
         ✗ Post response name
          ↳  99% — ✓ 8811 / ✗ 7

    默认情况下,失败的检查不会导致测试失败或中止,并且在一定程度上的失败是可以接受的,这取决于您的错误预算。

更简单的入门方式

Grafana Cloud 是开始使用指标、日志、追踪和仪表盘的最简单方式。我们提供慷慨的永久免费套餐和适用于各种用例的付费计划。