JS 性能测试与负载测试:`k6` / `Artillery` 模拟高并发场景

各位观众,晚上好!我是今天的性能测试和负载测试讲师,咱们今天的主题是“JS 性能测试与负载测试:k6 / Artillery 模拟高并发场景”。 放心,这不是枯燥的理论课,而是一场实战演练,我会用最接地气的方式,带大家一起探索如何用 k6Artillery 这两把利剑,模拟高并发场景,找出我们代码的瓶颈。

开场白:为什么要搞性能测试?

想象一下,你辛辛苦苦开发了一个电商网站,功能强大,界面精美,信心满满地发布上线。结果,双十一当天,流量像洪水一样涌来,网站直接崩溃!用户疯狂吐槽,订单丢失,老板脸色铁青……

这就是没有做性能测试的惨痛教训。

性能测试,就像给你的网站做体检,在高并发场景下,看看它能不能承受住压力,有没有哪里“虚弱”,需要“补补身子”。

所以,性能测试不是可有可无,而是上线前的必备环节,能帮你避免线上事故,保证用户体验。

主角登场:k6 vs Artillery

市面上有很多性能测试工具,今天我们重点介绍两款:k6Artillery。它们都是基于 JavaScript 开发,上手简单,功能强大,非常适合前端和 Node.js 开发者。

特性 k6 Artillery
脚本语言 JavaScript (ES6) YAML 或 JavaScript
易用性 相对简单,上手快 配置灵活,学习曲线稍陡峭
扩展性 通过扩展模块可以自定义指标和功能 通过插件可以扩展功能
报告 命令行报告、JSON 输出、Grafana 集成 命令行报告、HTML 报告、JSON 输出、Datadog 集成
社区支持 活跃,文档丰富 活跃,但相对 k6 稍弱
使用场景 适合快速构建和执行性能测试,监控 API 性能 适合复杂的场景模拟,集成 CI/CD 流程

简单来说,k6 就像一把瑞士军刀,轻巧灵活,适合快速测试;Artillery 更像是一套专业的工具箱,功能齐全,适合深度测试。

实战演练:用 k6 模拟高并发

咱们先来试试 k6。假设我们要测试一个简单的 API 接口,它返回一个 JSON 数据。

  1. 安装 k6

    根据你的操作系统,选择合适的安装方式。

  2. 编写测试脚本 (test.js)

    import http from 'k6/http';
    import { check, sleep } from 'k6';
    
    export const options = {
      vus: 10, // 虚拟用户数
      duration: '10s', // 测试持续时间
    };
    
    export default function () {
      const res = http.get('https://httpbin.test.k6.io/delay/1'); // 模拟 API 请求,延迟 1 秒
      check(res, {
        'status is 200': (r) => r.status === 200,
      });
      sleep(1); // 每次请求后休眠 1 秒
    }
    • vus: 虚拟用户数,模拟多少个用户同时发起请求。
    • duration: 测试持续时间,测试运行多久。
    • http.get(): 发起 HTTP GET 请求。
    • check(): 检查响应状态码是否为 200。
    • sleep(): 休眠一段时间,模拟用户行为。
  3. 运行测试

    在命令行中执行:

    k6 run test.js

    k6 会输出详细的测试报告,包括请求数量、响应时间、错误率等等。

进阶技巧:k6 的高级用法

  • 配置 stages (阶梯式加压)

    export const options = {
      stages: [
        { duration: '10s', target: 10 }, // 10 秒内逐渐增加到 10 个虚拟用户
        { duration: '20s', target: 20 }, // 20 秒内逐渐增加到 20 个虚拟用户
        { duration: '10s', target: 0 },  // 10 秒内逐渐减少到 0 个虚拟用户
      ],
    };

    stages 可以模拟更真实的流量场景,比如逐渐增加并发量,观察系统的表现。

  • 使用 thresholds (阈值)

    export const options = {
      thresholds: {
        'http_req_duration': ['p95<200'], // 95% 的请求响应时间必须小于 200ms
        'http_req_failed': ['rate<0.01'], // 请求失败率必须小于 1%
      },
    };

    thresholds 可以设置性能指标的阈值,如果超过阈值,测试会失败。

  • 读取环境变量

    const baseUrl = __ENV.BASE_URL || 'https://httpbin.test.k6.io'; // 从环境变量中读取 BASE_URL,如果没有则使用默认值
    
    export default function () {
      const res = http.get(`${baseUrl}/delay/1`);
    }

    通过环境变量,可以灵活配置测试参数,方便在不同环境运行测试。

  • 数据驱动测试 (Data-Driven Testing)

    import { SharedArray } from 'k6/data';
    import papaparse from 'https://jslib.k6.io/papaparse/5.1.0/index.js';
    import { check } from 'k6';
    import http from 'k6/http';
    
    // Load test data from CSV file
    const csvData = new SharedArray('data', function() {
      return papaparse.parse(open('./data.csv'), { header: true }).data;
    });
    
    export default function() {
      const data = csvData[Math.floor(Math.random() * csvData.length)];
      const res = http.get(`https://httpbin.test.k6.io/get?param1=${data.param1}&param2=${data.param2}`);
      check(res, {
        'status is 200': (r) => r.status === 200,
      });
    }

    假设 data.csv 文件包含 param1param2 两列数据,这个例子会从 CSV 文件中随机读取数据,作为 API 请求的参数。

实战演练:用 Artillery 模拟更复杂的场景

接下来,我们用 Artillery 模拟一个更复杂的场景,比如用户登录、浏览商品、加入购物车、下单支付。

  1. 安装 Artillery

    npm install -g artillery
  2. 编写测试脚本 (artillery.yml)

    config:
      target: 'https://httpbin.test.k6.io' # 目标 API 地址
      phases:
        - duration: 60 # 测试持续时间 60 秒
          arrivalRate: 20 # 每秒发送 20 个请求
      defaults:
        headers:
          Content-Type: 'application/json'
    scenarios:
      - name: 'Get Request with Delay'
        flow:
          - get:
              url: '/delay/1'
              capture:
                - json: '$.url'
                  as: request_url
          - log: "Request URL: {{request_url}}"
    • config.target: 目标 API 地址。
    • config.phases: 定义测试阶段,包括持续时间和每秒发送的请求数。
    • config.defaults.headers: 设置默认的请求头。
    • scenarios: 定义测试场景,每个场景包含一系列的 HTTP 请求。
    • flow: 定义场景的执行流程。
    • get: 发起 HTTP GET 请求。
    • capture: 从响应中提取数据,保存到变量中。
    • log: 打印日志。
  3. 运行测试

    在命令行中执行:

    artillery run artillery.yml

    Artillery 会输出详细的测试报告,包括请求数量、响应时间、错误率等等。

进阶技巧:Artillery 的高级用法

  • 使用 JavaScript 代码

    config:
      target: 'https://httpbin.test.k6.io'
      phases:
        - duration: 60
          arrivalRate: 20
    scenarios:
      - name: 'Post Request with Dynamic Data'
        flow:
          - function: "generateData" # 调用 JavaScript 函数
          - post:
              url: '/post'
              json:
                param1: "{{param1}}"
                param2: "{{param2}}"
    functions:
      generateData: |
        module.exports = {
          generateData: function(context, events, done) {
            context.vars['param1'] = Math.random().toString(36).substring(7); // 生成随机字符串
            context.vars['param2'] = Math.floor(Math.random() * 100); // 生成随机数字
            return done();
          }
        };

    通过 functions 字段,可以定义 JavaScript 函数,在测试流程中动态生成数据。

  • 使用 CSV 数据

    config:
      target: 'https://httpbin.test.k6.io'
      phases:
        - duration: 60
          arrivalRate: 20
      payload:
        path: 'data.csv'
        fields:
          - param1
          - param2
    scenarios:
      - name: 'Get Request with CSV Data'
        flow:
          - get:
              url: '/get?param1={{param1}}&param2={{param2}}'

    payload 字段可以指定 CSV 文件,Artillery 会从 CSV 文件中读取数据,作为 API 请求的参数。

  • 集成 CI/CD

    Artillery 可以方便地集成到 CI/CD 流程中,实现自动化性能测试。

    例如,可以在 Jenkins 中配置一个任务,在每次代码提交后自动运行 Artillery 测试,并根据测试结果决定是否允许部署。

如何选择:k6 还是 Artillery

没有绝对的答案,选择哪个工具取决于你的具体需求。

  • 如果你需要快速构建和执行简单的性能测试,k6 是一个不错的选择。 它上手简单,文档丰富,可以快速帮你找到 API 的性能瓶颈。

  • 如果你需要模拟更复杂的场景,比如用户行为流程,或者需要集成 CI/CD 流程,Artillery 更适合你。 它的配置灵活,可以满足各种复杂的测试需求。

  • 如果你对 JavaScript 非常熟悉,并且需要自定义指标和功能,k6 的扩展性更强。

  • 如果你需要生成更美观的 HTML 报告,Artillery 的报告功能更强大。

总结:性能测试的 Tips

  • 尽早开始性能测试。 在开发阶段就开始进行性能测试,可以及早发现问题,避免后期修复成本过高。

  • 模拟真实的流量场景。 性能测试的目的是模拟真实的用户行为,所以要尽量模拟真实的流量场景,包括并发量、请求类型、用户行为等等。

  • 监控系统资源。 在进行性能测试的同时,要监控系统的资源使用情况,包括 CPU、内存、磁盘 I/O 等等,找出性能瓶颈。

  • 分析测试结果。 性能测试的目的是找出性能瓶颈,所以要认真分析测试结果,找出导致性能问题的根本原因,并进行优化。

  • 自动化性能测试。 将性能测试集成到 CI/CD 流程中,实现自动化性能测试,可以保证代码质量,避免线上事故。

最后的最后:性能优化永无止境

性能优化是一个持续不断的过程,没有最好,只有更好。希望今天的讲座能帮助大家掌握性能测试的基本技能,为构建高性能、高可用的系统打下坚实的基础。

记住,性能测试不是一次性的任务,而是一个持续的过程。只有不断地测试、分析、优化,才能让你的系统跑得更快、更稳!

谢谢大家!祝大家编码愉快,性能测试顺利!

问答环节

现在是问答环节,大家有什么问题可以提出来,我会尽力解答。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注