各位观众,晚上好!我是今天的性能测试和负载测试讲师,咱们今天的主题是“JS 性能测试与负载测试:k6
/ Artillery
模拟高并发场景”。 放心,这不是枯燥的理论课,而是一场实战演练,我会用最接地气的方式,带大家一起探索如何用 k6
和 Artillery
这两把利剑,模拟高并发场景,找出我们代码的瓶颈。
开场白:为什么要搞性能测试?
想象一下,你辛辛苦苦开发了一个电商网站,功能强大,界面精美,信心满满地发布上线。结果,双十一当天,流量像洪水一样涌来,网站直接崩溃!用户疯狂吐槽,订单丢失,老板脸色铁青……
这就是没有做性能测试的惨痛教训。
性能测试,就像给你的网站做体检,在高并发场景下,看看它能不能承受住压力,有没有哪里“虚弱”,需要“补补身子”。
所以,性能测试不是可有可无,而是上线前的必备环节,能帮你避免线上事故,保证用户体验。
主角登场:k6
vs Artillery
市面上有很多性能测试工具,今天我们重点介绍两款:k6
和 Artillery
。它们都是基于 JavaScript 开发,上手简单,功能强大,非常适合前端和 Node.js 开发者。
特性 | k6 | Artillery |
---|---|---|
脚本语言 | JavaScript (ES6) | YAML 或 JavaScript |
易用性 | 相对简单,上手快 | 配置灵活,学习曲线稍陡峭 |
扩展性 | 通过扩展模块可以自定义指标和功能 | 通过插件可以扩展功能 |
报告 | 命令行报告、JSON 输出、Grafana 集成 | 命令行报告、HTML 报告、JSON 输出、Datadog 集成 |
社区支持 | 活跃,文档丰富 | 活跃,但相对 k6 稍弱 |
使用场景 | 适合快速构建和执行性能测试,监控 API 性能 | 适合复杂的场景模拟,集成 CI/CD 流程 |
简单来说,k6
就像一把瑞士军刀,轻巧灵活,适合快速测试;Artillery
更像是一套专业的工具箱,功能齐全,适合深度测试。
实战演练:用 k6
模拟高并发
咱们先来试试 k6
。假设我们要测试一个简单的 API 接口,它返回一个 JSON 数据。
-
安装
k6
根据你的操作系统,选择合适的安装方式。
- macOS:
brew install k6
- Linux:
sudo apt-get update && sudo apt-get install k6
- Windows: 下载安装包:https://k6.io/docs/getting-started/installation/
- macOS:
-
编写测试脚本 (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()
: 休眠一段时间,模拟用户行为。
-
运行测试
在命令行中执行:
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}¶m2=${data.param2}`); check(res, { 'status is 200': (r) => r.status === 200, }); }
假设
data.csv
文件包含param1
和param2
两列数据,这个例子会从 CSV 文件中随机读取数据,作为 API 请求的参数。
实战演练:用 Artillery
模拟更复杂的场景
接下来,我们用 Artillery
模拟一个更复杂的场景,比如用户登录、浏览商品、加入购物车、下单支付。
-
安装
Artillery
npm install -g artillery
-
编写测试脚本 (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
: 打印日志。
-
运行测试
在命令行中执行:
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}}¶m2={{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 流程中,实现自动化性能测试,可以保证代码质量,避免线上事故。
最后的最后:性能优化永无止境
性能优化是一个持续不断的过程,没有最好,只有更好。希望今天的讲座能帮助大家掌握性能测试的基本技能,为构建高性能、高可用的系统打下坚实的基础。
记住,性能测试不是一次性的任务,而是一个持续的过程。只有不断地测试、分析、优化,才能让你的系统跑得更快、更稳!
谢谢大家!祝大家编码愉快,性能测试顺利!
问答环节
现在是问答环节,大家有什么问题可以提出来,我会尽力解答。