关于 load-testing-commerce
面向开发者和电商团队的实用技能,帮助为店铺流量高峰做准备。内容涵盖使用 k6 和 Artillery 模拟购物流程、平台专项检查及结果分析。
主要功能
- 01模拟真实购物流程
- 02k6 与 Artillery 使用指导
- 03按平台制定测试方法
- 04性能基线与结果分析
- 05测试环境及数据安全实践
适用场景
- 01为季节性促销准备店铺
- 02发布前检查结账性能
- 03在预发布环境复现负载故障
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill load-testing-commerce安装后在 Claude Code 中输入/load-testing-commerce确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
tile.jsonevals/artillery-api-load-test/criteria.jsontask.mdevals/baseline-tracking-and-regression-detecti/criteria.jsontask.mdevals/k6-realistic-checkout-scenario/criteria.jsontask.md详细介绍
负载测试 — Commerce
概述
对电商应用进行负载测试,不只是用并发请求猛击某个端点。逼真的测试场景会模拟实际用户行为:浏览商品目录、搜索商品、将商品加入购物车并完成结账,同时包含各个操作之间的思考时间。本技能涵盖如何在 k6 和 Artillery 中构建逼真的购物场景、解读结果以找到瓶颈,以及在大型促销活动前建立性能基线。
何时使用此技能
- 准备限时抢购、黑色星期五或季节性流量高峰时
- 部署重大基础设施变更(新数据库、CDN、结账服务)时
- 为新网店建立性能 SLO 和基线时
- 生产事故由负载导致,且需要在预发布环境中重现时
- 即将发布新的结账功能,但其性能影响尚不明确时
核心说明
第 1 步:确定你的平台以及可测试的内容
| 平台 | 负载测试范围 | 推荐方法 |
|---|---|---|
| Shopify | Shopify 的基础设施会自动扩展,因此无法对其施加有意义的过载 | 使用 Google PageSpeed Insights 和 Lighthouse CI 测试主题的前端性能;单独测试你托管的自定义应用或店面 |
| WooCommerce | 服务器由你负责,因此销售活动前进行负载测试至关重要 | 使用 Loader.io(免费层级:1 个目标,每次测试 10K 个连接)测试预发布网站;或使用 k6/Artillery 进行详细的场景测试 |
| BigCommerce | BigCommerce 会自动扩展,平台基础设施不是需要担心的问题 | 使用 PageSpeed Insights 测试主题的前端性能;测试你托管的任何自定义中间件或无头层 |
| 自定义 / 无头 | 可完全掌控,负载测试由你负责 | 使用 k6(开源、可编写脚本)或 Artillery(基于 YAML),针对预发布环境运行逼真的购物场景 |
第 2 步:针对平台进行负载测试
Shopify
Shopify 的基础设施几乎可以应对任何流量高峰。负载测试应聚焦于前端体验:
-
使用 Google PageSpeed Insights 测试最关键的页面(商品页面、商品集合页面、结账页面):
- 前往 pagespeed.web.dev,测试商品页面和商品集合页面
- 移动端 Performance 分数目标为 75+;促销活动前处理所有红色或橙色建议
-
查看 Shopify 内置的性能报告:
- 前往 Online Store → Themes,然后点击 View report
- 该报告根据真实用户数据展示网店的 Core Web Vitals(LCP、CLS、FID)
- 移动端 LCP 分数较差时,几乎总是意味着主视觉图片需要设置
fetchpriority="high"或进行压缩
-
促销活动前检查已安装的应用:
- 前往 Shopify 管理后台中的 Apps,检查所有会向店面注入脚本的应用
- 每个第三方脚本都会增加 50–200ms;移除未使用的应用,并停用任何同步加载的应用
-
对于 Shopify Plus——重大上线前通知 Shopify 支持团队:
- 至少在活动开始前 48 小时,通过 Plus 支持渠道提交限时抢购通知
- Shopify 可以预先分配资源,并在活动期间监控你的网店
WooCommerce
WooCommerce 运行在你的托管基础设施上。请在与生产配置一致的预发布环境中进行测试(相同的 PHP 版本、MySQL 版本和缓存堆栈)。
使用 Loader.io 快速进行负载测试(无需编写代码):
- 在 loader.io 注册(免费套餐:1 个目标,每次测试最多 10K 个连接)
- 点击 New Test,输入你的预发布环境 URL
- 配置渐进式测试:从 0 个客户端开始,在 60 秒内逐步增加到预期峰值流量,并保持 120 秒
- Loader.io 会提供验证令牌——将其添加到预发布站点的某个页面,以确认所有权
- 专门针对结账页面运行测试——商品浏览通常会命中缓存;结账则会访问数据库
解读结果:
- 峰值时响应时间低于 3 秒:可以接受
- 错误率高于 1%:检查服务器日志,排查 PHP 致命错误、数据库超时或内存耗尽
- 如果服务器在低于预期的流量下就吃力:请在促销前升级托管套餐或添加 Redis Object Cache
测试前,请确保预发布环境的堆栈已正确配置:
- Redis Object Cache 已启用(Settings → Redis — 状态显示为绿色)
- WP Rocket 页面缓存已启用,且访问量最高的商品页面已完成预热
- 测试所用的服务器规格与生产环境相同
Custom / Headless
对于自定义店面,请使用 k6 或 Artillery,在与生产环境一致的预发布环境中进行测试。
开始前的重要事项:
- 始终在预发布环境中测试,绝不在生产环境中测试
- 使用测试模式支付令牌(Stripe:
tok_visa)——绝不要针对真实支付处理服务运行负载测试 - 使用容易识别的电子邮件域名标记测试订单(例如
@test-load.invalid),以便之后批量删除 - 为测试商品预留无限库存,避免结账场景因超卖而失败
使用 k6 模拟真实购物场景:
k6 使用 ramping-arrival-rate 执行器控制每秒请求数(比基于 VU 的方法更贴近实际)。典型的电子商务流量分布:60% 浏览、25% 商品详情、10% 购物车、5% 结账。
// k6/commerce-load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';
const BASE_URL = __ENV.BASE_URL || 'https://staging.mystore.com';
const products = new SharedArray('products', function() {
return JSON.parse(open('./data/products.json')); // array of {id, defaultVariantId}
});
export const options = {
scenarios: {
catalog_browsing: {
executor: 'ramping-arrival-rate',
startRate: 0, timeUnit: '1s',
preAllocatedVUs: 50, maxVUs: 200,
stages: [
{ target: 60, duration: '2m' }, // Ramp up
{ target: 60, duration: '5m' }, // Steady state
{ target: 0, duration: '1m' }, // Ramp down
],
exec: 'catalogBrowsing',
},
checkout_flow: {
executor: 'ramping-arrival-rate',
startRate: 0, timeUnit: '1s',
preAllocatedVUs: 10, maxVUs: 50,
stages: [
{ target: 5, duration: '2m' },
{ target: 5, duration: '5m' },
{ target: 0, duration: '1m' },
],
exec: 'checkoutFlow',
},
},
thresholds: {
'http_req_duration{scenario:checkout_flow}': ['p(95)<3000'],
'http_req_failed{scenario:checkout_flow}': ['rate<0.01'],
'http_req_duration{scenario:catalog_browsing}': ['p(95)<1000'],
},
};
export function catalogBrowsing() {
http.get(`${BASE_URL}/api/collections/featured`, { tags: { step: 'homepage' } });
sleep(2 + Math.random() * 3); // Think time: 2–5s
const categories = ['t-shirts', 'hoodies', 'accessories'];
const category = categories[Math.floor(Math.random() * categories.length)];
http.get(`${BASE_URL}/api/collections/${category}?page=1&sort=popular`, { tags: { step: 'category' } });
sleep(3 + Math.random() * 5);
}
export function checkoutFlow() {
const product = products[Math.floor(Math.random() * products.length)];
// View product
const productRes = http.get(`${BASE_URL}/api/products/${product.id}`, { tags: { step: 'view_product' } });
check(productRes, { 'product page 200': r => r.status === 200 });
sleep(2 + Math.random() * 3);
// Add to cart
const cartRes = http.post(`${BASE_URL}/api/cart`, JSON.stringify({
items: [{ productId: product.id, variantId: product.defaultVariantId, quantity: 1 }],
}), { headers: { 'Content-Type': 'application/json' }, tags: { step: 'add_to_cart' } });
check(cartRes, { 'add to cart 200': r => r.status === 200 });
const cart = JSON.parse(cartRes.body);
sleep(1 + Math.random() * 2);
// Start checkout
const checkoutRes = http.post(`${BASE_URL}/api/checkout/start`, JSON.stringify({
cartId: cart.id,
customer: { email: `test-${Math.random().toString(36).slice(7)}@test-load.invalid` },
}), { headers: { 'Content-Type': 'application/json' }, tags: { step: 'start_checkout' } });
check(checkoutRes, { 'checkout start 200': r => r.status === 200 });
const checkout = JSON.parse(checkoutRes.body);
sleep(5 + Math.random() * 10); // Think time: filling form
// Place order
const orderRes = http.post(`${BASE_URL}/api/checkout/complete`, JSON.stringify({
checkoutId: checkout.id,
paymentToken: 'tok_visa', // Stripe test token
shippingMethodId: checkout.shippingMethods[0]?.id,
}), { headers: { 'Content-Type': 'application/json' }, tags: { step: 'place_order' } });
check(orderRes, { 'order placed 201': r => r.status === 201 });
}
Artillery YAML 配置(作为 k6 的替代方案,适合以 API 为重点的测试):
# artillery/commerce-load-test.yml
config:
target: "{{ $processEnvironment.BASE_URL }}"
phases:
- name: "Warm-up"
duration: 60
arrivalRate: 5
- name: "Ramp up"
duration: 120
arrivalRate: 5
rampTo: 50
- name: "Peak load"
duration: 300
arrivalRate: 50
- name: "Spike"
duration: 30
arrivalRate: 200
- name: "Recovery"
duration: 60
arrivalRate: 20
processor: "./processors/commerce-helpers.js"
scenarios:
- name: "Browse catalog"
weight: 60
flow:
- get:
url: "/api/collections/all?page=1"
capture:
json: "$.products[0].id"
as: "productId"
- think: 3
- get:
url: "/api/products/{{ productId }}"
- name: "Complete checkout"
weight: 10
flow:
- function: "generateCheckoutData"
- post:
url: "/api/cart"
json:
productId: "{{ productId }}"
quantity: 1
capture:
json: "$.id"
as: "cartId"
- think: 8
- post:
url: "/api/checkout/start"
json:
cartId: "{{ cartId }}"
email: "{{ email }}"
capture:
json: "$.checkoutId"
as: "checkoutId"
- think: 10
- post:
url: "/api/checkout/complete"
json:
checkoutId: "{{ checkoutId }}"
paymentToken: "tok_visa"
expect:
- statusCode: 201
运行测试并采集基线数据:
k6 run \
--env BASE_URL=https://staging.mystore.com \
--out json=results/baseline-$(date +%Y%m%d).json \
k6/commerce-load-test.js
使用标签按步骤分析结果:
# Overall summary (k6 outputs this automatically at end of run)
# Per-step breakdown from the JSON output
cat results/baseline-*.json | jq '
[.data_points[] | select(.type=="Point" and .metric=="http_req_duration")]
| group_by(.tags.step)
| map({step: .[0].tags.step, p95_ms: (map(.value) | sort | .[length * 0.95 | floor])})
'
电子商务的扩展目标:
- 目录页面 p95 < 1000ms
- 商品详情 p95 < 1500ms
- 结账流程 p95 < 3000ms
- 峰值时错误率 < 1%
在重大版本发布前于 CI 中运行负载测试(GitHub Actions):
# .github/workflows/load-test.yml
name: Load Test (Pre-Release)
on:
workflow_dispatch:
inputs:
target_url:
description: 'Staging URL to test'
required: true
default: 'https://staging.mystore.com'
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install k6
run: |
curl -s https://dl.k6.io/key.gpg | sudo apt-key add -
echo "deb https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update && sudo apt-get install k6
- name: Run load test
env:
BASE_URL: ${{ github.event.inputs.target_url }}
run: k6 run --env BASE_URL=$BASE_URL --out json=results.json k6/commerce-load-test.js
- uses: actions/upload-artifact@v4
with:
name: load-test-results
path: results.json
最佳实践
- 模拟步骤之间的思考时间——真实用户会在操作之间停顿;去掉思考时间会产生不切实际的高请求率,无法反映生产环境中的使用模式
- 使用测试模式支付令牌——始终使用 Stripe 的
tok_visa或同等的测试令牌;绝不要针对真实支付处理服务运行负载测试 - 在预发布环境而非生产环境中测试——负载测试会消耗资源,并可能影响真实客户的服务体验
- 按照预期峰值的 1.5 倍、2 倍和 3 倍进行测试——在实际促销前,以不同负载级别运行多次测试,找出系统的拐点
- 在测试期间监控数据库和缓存——留意数据库连接池耗尽、Redis 驱逐以及数据库中的锁等待时间;应用层吞吐量看起来可能正常,但数据库或许已经饱和
- 为测试订单添加标记,以便轻松清理——使用统一的测试电子邮件域名(
@test-load.invalid),以便在测试运行后批量删除测试数据:DELETE FROM orders WHERE email LIKE '%@test-load.invalid'
常见陷阱
| 问题 | 解决方案 |
|---|---|
| 商品售罄,导致结账场景失败 | 预留库存不限量的测试商品;使用单独的 is_load_test_product 标记,并在真实商品目录页面中将其过滤掉 |
| Loader.io 测试通过,但生产环境仍然变慢 | 预发布环境可能无法反映生产环境的负载情况——请确认 Redis Object Cache 已启用、MySQL 版本一致,并且服务器规格相同 |
| k6 的 VU 耗尽,尚未达到目标 RPS | 使用 ramping-arrival-rate 执行器(控制 RPS),而不是 ramping-vus(控制并发用户数);增大 preAllocatedVUs 和 maxVUs |
| 计划中的负载测试期间告警过多 | 在监控系统中添加负载测试标记(Datadog 标签、Grafana 注释),以便在预定测试时段内抑制非关键告警 |
| 不同运行轮次的结果无法复现 | 使用包含固定数据集文件的 SharedArray,而不是使用 Math.random() 生成商品;在运行前固定测试数据集 |
相关技能
- @flash-sale-scaling
- @monitoring-alerting-commerce
- @database-optimization-commerce
- @bot-protection