Artillery 性能测试:HTTP 与 WebSocket 负载

FreeGuideOnline 最新 2026-06-16

Artillery 性能测试实战:从 HTTP 到 WebSocket 全场景负载

在现代应用体系中,性能测试早已不是可选项,而是交付质量的关键防线。Artillery 作为一款开源的 Node.js 性能测试工具,凭借声明式配置、多协议支持和极低的上手门槛,迅速成为开发团队的主流选择。本教程将带你从零开始,掌握 Artillery 的核心概念,并分别完成 HTTP 接口负载测试WebSocket 长连接压力测试

为什么选择 Artillery?

市面上的压测工具很多,Artillery 的独特优势在于:

  • 声明式 YAML 配置:无需编写脚本,一份配置文件即可描述所有测试场景。
  • 原生支持 HTTP/HTTPS、WebSocket、Socket.io、Playwright:一套工具覆盖 API、实时通信、端到端浏览器测试。
  • 内置性能指标:自动采集延迟百分位数(p50/p95/p99)、并发数、请求速率、错误率等关键数据。
  • 扩展性强:通过插件机制可自定义引擎、报告器,并能与 CI/CD 管道无缝集成。
  • 完全免费开源:无任何功能限制,适合个人开发者及企业团队。

接下来,我们将通过环境安装、HTTP 测试编写、WebSocket 测试实现三个环节,带你系统性掌握 Artillery 性能测试。

环境安装与快速验证

Artillery 基于 Node.js,请确保你的环境已安装 Node.js 18+。如需管理 Node.js 版本,推荐使用 nvm

# 全局安装 Artillery
npm install -g artillery@latest

# 验证安装
artillery version

看到版本信息输出即表示安装成功。推荐使用最新版的 Artillery(v2+),它带来了性能优化和更清晰的命令结构。

第一节:HTTP 负载测试从入门到进阶

HTTP 测试是性能测试中最常见的场景。我们将从一个简单的 API 压测开始,逐步扩展到多阶段、数据驱动的复杂场景。

基础测试脚本:http-basic.yml

创建一个 http-basic.yml 文件,用极简的 YAML 定义你的第一个测试:

config:
  target: "https://httpbin.org"  # 目标基础地址
  phases:
    - duration: 30               # 测试持续30秒
      arrivalRate: 10            # 每秒创建10个新虚拟用户
      name: "热身阶段"
scenarios:
  - name: "获取IP并发布数据"
    flow:
      - get:
          url: "/ip"
      - post:
          url: "/post"
          json:
            username: "artillery_user"
            timestamp: "{{ $timestamp }}"

脚本说明

  • config.target:所有请求的公共前缀,避免在每一步中重复书写完整 URL。
  • phases:定义负载注入的“阶段”,此处为单一阶段,持续 30 秒,每秒到达 10 个虚拟用户(VU)。这意味着大约总请求数为 30×10 = 300 次。
  • scenarios:虚拟用户执行的具体操作。这里按顺序执行一个 GET 请求和一个 POST 请求,POST 请求体使用 Artillery 内置函数 $timestamp 生成动态时间戳。

执行测试并查看报告

使用 run 命令启动测试:

artillery run http-basic.yml

Artillery 会实时打印中间统计,并在测试结束时输出类似下面的汇总报告:

--------------------------------------
Summary report @ 10:15:23(+0800)
--------------------------------------

http.codes.200: .......................... 600
http.request_rate: ........................ 20/sec
http.requests: ............................ 600
http.response_time:
  min: ..................................... 80
  max: ..................................... 320
  median: .................................. 150
  p95: ..................................... 280
  p99: ..................................... 300
vusers.created: ............................ 300
vusers.failed: .............................. 0

关键指标解读:

  • http.request_rate:每秒完成的请求数,接近 arrivalRate × 场景请求数(本例中为 10×2=20)。
  • 响应时间百分位数:p95 小于 300ms 表示 95% 的请求在 300ms 以内完成,这是判断性能是否达标的黄金指标。
  • vusers.failed:失败虚拟用户数,应尽早关注并归零。

进阶 HTTP 配置:多阶段负载与动态数据

现实中的流量是变化的。Artillery 支持多阶段负载,可以模拟突增、平稳、衰减等复杂模式。此外还支持从 CSV 文件加载测试数据,实现参数化。

config:
  target: "https://jsonplaceholder.typicode.com"
  phases:
    - duration: 60
      arrivalRate: 5
      name: "低负载预热"
    - duration: 120
      arrivalRate: 20
      name: "持续高峰"
    - duration: 60
      arrivalRate: 5
      name: "逐步退出"
  payload:
    path: "users.csv"
    fields:
      - "userId"
      - "userName"
    order: sequence   # 按顺序循环取值
scenarios:
  - name: "用户信息查询"
    flow:
      - get:
          url: "/users/{{ userId }}"
          capture:
            - json: "$.name"
              as: "apiUserName"
      - think: 1       # 模拟用户思考时间1秒
      - log: "API返回的用户名: {{ apiUserName }},CSV中的用户名: {{ userName }}"

新增要点:

  • phases 数组:定义3个阶段,总计240秒。阶段间的切换是平滑的,Artillery 会自动调整虚拟用户创建速率。
  • payload:从 users.csv 文件加载数据。CSV 第一行为列名,后续行为数据行。在请求中通过 {{ userId }} 引用。
  • capture:从响应中提取数据。这里捕获 JSON 路径 $.name 的值并存入变量 apiUserName,后续请求或日志均可使用。
  • think:让虚拟用户在步骤间等待固定时间,更贴近真实用户行为。

执行该脚本前,请在同目录下创建 users.csv 文件:

userId,userName
1,Delphine
2,Ervin
3,Clementine

第二节:WebSocket 性能测试实战

对于聊天应用、实时协作工具、游戏服务器等场景,单纯的 HTTP 测试远远不够。Artillery 原生支持 WebSocket 协议,能够模拟长连接建立、消息发送与接收、连接关闭的全生命周期压力。

WebSocket 测试基础脚本

以下脚本连接到一个公开的 WebSocket 回显服务 wss://echo.websocket.org,发送消息并验证收到的回显。

config:
  target: "wss://echo.websocket.org"
  phases:
    - duration: 30
      arrivalRate: 5
  ws:
    # 可选的全局WebSocket配置,如设置子协议等
scenarios:
  - name: "回显压力测试"
    engine: "ws"
    flow:
      - connect: "/"     # 建立WebSocket连接,路径为根路径
      - send: "Hello Artillery {{ $randomString() }}"
      - think: 0.5
      - send: "第二条消息"
      - think: 1
      - close: 1000      # 正常关闭连接(状态码1000)

核心说明

  • engine: "ws":必须为基于 WebSocket 的场景显式指定引擎,否则默认使用 HTTP 引擎。
  • connect:第一个动作通常是 connect,它发起 WebSocket 握手。后面可以接 sendthinkclose 等动作。
  • 连接复用:一个虚拟用户在一次场景迭代中只建立一个连接,可以连续发送多条消息,然后关闭。Artillery 会记录整个连接期间的消息延迟。

验证上行与下行消息

真实测试中,你往往需要关注“我发出消息后,多久能收到服务端的回应?”Artillery 的 ws 引擎支持消息匹配与响应时间捕获

scenarios:
  - name: "带响应验证的WS测试"
    engine: "ws"
    flow:
      - connect: "/chat"
      - send:
          data: "{\"type\":\"login\",\"user\":\"tester\"}"
        capture:
          - json: "$.status"
            as: "loginStatus"
        match:
          json: "$.status"
          value: "ok"
      - think: 0.5
      - send:
          data: "ping"
        capture:
          - regex: "pong"
            as: "pongResponse"
        match:
          regex: "pong"

此脚本做了两件关键的事:

  1. capture:与 HTTP 类似,从 WebSocket 接收的下一条消息中提取数据。这里既可以按 JSON 路径捕获,也可以用正则表达式捕获文本。
  2. match:断言收到的消息必须满足的条件。如果服务端返回的消息不符合预期,Artillery 会将此视为错误,并在报告中体现。

注意capturematch 作用于发送 send 后接收到的第一个服务端消息。如果服务端会返回多条响应,你需要将逻辑拆分为多个 send,每个 send 捕获对应的响应。

高级 WebSocket 场景:多用户顺序交互

在某些实时应用中,你需要模拟用户 A 发送消息后,用户 B 收到广播。Artillery 的 beforeScenarioafterScenario 钩子可以配合自定义处理器实现,但简单场景下也可以利用“顺序连接”来近似模拟。以下是两个虚拟用户模拟对话的简例:

scenarios:
  - name: "用户对话模拟"
    engine: "ws"
    flow:
      - connect: "/room/general"
      - send: "{\"event\":\"join\",\"room\":\"general\"}"
      - think: 1
      - send: "Hello everyone!"
      - think: 2
      # 发送第二条带捕获的消息,验证服务端是否广播回自己
      - send: "How are you?"
        capture:
          - json: "$.broadcastMessage"
            as: "broadcast"
        match:
          json: "$.broadcastMessage"
          value: "How are you?"
      - close:
          code: 1001

上述场景中,服务端需要将消息广播回发送者自身,才能匹配 match 断言。通过这种方式,你可以验证服务端在大量并发连接下的广播能力与延迟。

第三节:编写复杂场景与负载模型

场景权重与混合流量

当你的应用同时存在读和写操作,且读写比例不同时,可以通过 scenarios 数组 + weight 字段实现流量混合。

config:
  target: "https://api.myservice.com"
  phases:
    - duration: 120
      arrivalRate: 50
scenarios:
  - name: "读操作 - 浏览商品"
    weight: 7
    flow:
      - get:
          url: "/products?page={{ $randomNumber(1,10) }}"
  - name: "写操作 - 提交订单"
    weight: 3
    flow:
      - post:
          url: "/orders"
          json:
            productId: "{{ $randomNumber(1,1000) }}"
            quantity: 1

此处每秒创建的 50 个虚拟用户中,平均有 70% 执行商品浏览(读操作),30% 执行订单提交(写操作)。weight 不必归一化,Artillery 会根据权重比例自动分配。

使用 beforeafter 钩子

若需要在场景开始前执行一次性操作(如登录获取 token),可以使用全局处理器。创建 processors.js

// processors.js
module.exports = {
  generateAuthToken: function (userContext, events, done) {
    // 可在此处调用API获取token,然后写入userContext.vars
    userContext.vars.token = 'bearer-secret-token-123';
    return done();
  }
};

在脚本中引用:

config:
  target: "https://secure-api.example.com"
  processor: "./processors.js"
  phases:
    - duration: 60
      arrivalRate: 10
scenarios:
  - name: "已认证请求"
    flow:
      - function: "generateAuthToken"   # 每个虚拟用户都会执行一次
      - get:
          url: "/secure-data"
          headers:
            Authorization: "Bearer {{ token }}"

function 步骤允许你在虚拟用户的执行流中插入自定义逻辑,这对于需要动态令牌、签名计算的场景非常重要。

第四节:生成结构化报告与分析

命令行输出虽然即时,但不便于存档和分享。Artillery 内置了 HTML 报告生成器,只需在测试时启用记录。

artillery run --output report.json http-basic.yml
artillery report --output report.html report.json

执行完毕后,用浏览器打开 report.html 你会看到:

  • 聚合统计:总请求数、平均响应时间、错误率等。
  • 百分位分布图:直观展示响应时间的 p50、p75、p90、p95、p99。
  • 并发数与请求速率的时间序列:便于定位性能拐点。
  • 错误详情:列出所有失败及其原因。

你还可以通过插件将结果输出到 Datadog、Prometheus、CloudWatch 等监控系统,实现持续性能回归测试。

常见问题与排错

1. 连接超时或被拒

如果看到 ECONNREFUSED 或大量超时错误,请检查:

  • 目标地址是否可从测试机访问(防火墙、VPN)。
  • 是否达到了服务端的最大连接数限制。
  • 请求路径是否正确(WebSocket 的 connect 路径易写错)。

2. 虚拟用户始终为0

可能是 arrivalRate 太低或 duration 很短导致统计窗口未更新。增大速率并延长测试时间观察。

3. WebSocket 消息匹配失败

确保 capture 中的 JSON 路径或正则表达式与收到的实际消息格式完全一致。建议先用 - log: "{{}}" 打印收到的原始消息进行调试。

4. 内存不足

极高并发(如数千 VU)可能导致 Node.js 内存溢出。可以:

  • 增加 Node.js 堆内存:NODE_OPTIONS="--max-old-space-size=4096" artillery run ...
  • 使用 artillery run --scenario-name 只运行特定场景。
  • 考虑分布式执行(Artillery Pro 特性,但开源版可用多个实例手工分摊)。

总结与下一步

至此,你已经能够独立完成基于 Artillery 的 HTTP 和 WebSocket 性能测试。回顾一下关键知识点:

  1. 通过 YAML 配置 定义目标、负载阶段和虚拟用户行为。
  2. 利用 phases 设计阶梯型、锯齿型或持续型压力曲线。
  3. 在场景中灵活使用 动态变量、数据文件、响应捕获与断言 来模拟真实业务。
  4. 通过 HTML 报告分析延迟分布,定位性能瓶颈。

Artillery 的官方文档(docs.artillery.io)包含了插件开发、PaaS 集成、Socket.io 引擎等高级主题。性能测试是一项持续实践,建议将 Artillery 脚本纳入 CI 流水线,每次代码变更后自动执行,守护应用性能基线。

现在,打开终端,编写你的第一份压测脚本,开始测量真实世界的性能吧!