
性能测试接口测试CLI【免费下载链接】artilleryThe complete load testing platform. Everything you need for production-grade load tests. Serverless distributed. Load test with Playwright. Load test HTTP APIs, GraphQL, WebSocket, and more. Use any Node.js module.项目地址https://gitcode.com/gh_mirrors/ar/artillery点击查看免费下载本篇技术指南基于 Artillery 官方示例 using-data-from-redis讲解如何在并发与分布式压测场景下借助 Redis 为每个虚拟用户VU分配唯一测试数据。读完本文你将掌握用beforeScenario钩子配合LPOP从 Upstash Redis 拉取唯一用户名/密码的完整流程理解 CSV 数据源在并发下的唯一性缺陷并能独立搭建可复制的Redis 数据池压测方案。为什么 CSV 数据在并发压测中不够用Artillery 天然支持从 CSV 文件注入测试数据config.payload也是多数压测脚本的首选。但 Artillery 的并发模型决定了当多个虚拟用户同时读取同一份 CSV 数据时无法保证每个用户取到的是互不重复的唯一数据尤其是运行分布式压测多个 worker 各自维护一份 CSV时重复取数几乎不可避免。这在登录压测、账号绑定类场景中尤为致命——例如你要用 10 万个真实存在的用户凭证做登录压测若两个 VU 同时使用同一个账号可能会触发服务端会话互踢、风控拦截或数据污染直接干扰压测结果。此时 Redis 是一个简单而可靠的替代方案将用户数据预先写入 Redis 列表虚拟用户每次通过原子操作LPOP弹出队首元素天然保证每个 VU 拿到的数据全局唯一、互不重复且无需在多个 worker 之间做复杂的状态同步。你可以使用自建 Redis如 AWS ElastiCache也可以使用 Upstash 这类托管方案——示例 README 推荐 Upstash理由是它简单且天然 Serverless无需维护服务器。示例整体架构与工作流程本示例位于 examples/using-data-from-redis由四个核心文件组成职责清晰文件作用scripts/seed-redis-with-users.js独立的数据播种脚本生成 100 个随机用户名/密码并写入 Redisprocessor.js处理器函数getUser在每个场景开始前从 Redis 弹出一个用户scenario.ymlArtillery 压测脚本通过beforeScenario挂载处理器.env.sample环境变量模板UPSTASH_REDIS_URL与UPSTASH_REDIS_TOKEN工作流程如下播种Seed用npm run seed运行独立脚本将 100 个自动生成的用户用户名 密码以 JSON 字符串形式LPUSH进 Redis 的users列表拉取Pop压测运行时每个 VU 的场景执行前beforeScenario: getUser钩子调用 Redis 的lpop(users, 1)原子弹出一个用户使用Use弹出用户的username、password写入context.vars场景 flow 通过模板语法{{ username }}、{{ password }}引用示例中直接打印到控制台以证明数据唯一。由于LPOP是原子操作无论多少 VU 并发、无论分布式 worker 有多少个每个用户数据都只会被弹出一次从根本上解决了并发取重的难题。前置条件与运行步骤前置条件注册一个 Upstash 账号按 Upstash 官方快速入门指南创建 Redis 实例从控制台 UI 获取该实例的endpointURL与token后续填入.env。运行步骤准备环境变量在本目录创建.env文件内容与.env.sample保持一致填入你自己的 endpoint 与 tokenUPSTASH_REDIS_URL UPSTASH_REDIS_TOKEN安装依赖在本目录执行npm install。依赖清单见 package.json运行时依赖upstash/redisRedis 客户端开发依赖ngneat/falso生成随机用户名/密码与dotenv加载.env。播种数据执行npm run seed向 Redis 写入 100 个自动生成的用户。脚本内部以USERS_COUNT 100、BATCH_SIZE 5分批写入每批 5 个避免一次性构造 100 个对象带来的内存峰值并逐批打印日志便于观察进度。运行压测执行npm run test等价于npx artillery run scenario.yml --dotenv .env即可看到每个 VU 打印出的唯一用户名与密码。逐文件拆解从播种到消费的完整实现播种脚本批量生成并写入 Redisscripts/seed-redis-with-users.js 的核心逻辑分三步① 初始化客户端并生成用户const redis new Redis({ url: process.env.UPSTASH_REDIS_URL, token: process.env.UPSTASH_REDIS_TOKEN, }); function generateUser() { return { username: falso.randUserName(), password: falso.randPassword(), }; }用户名与密码由ngneat/falso生成格式贴近真实数据如常见英文名、强随机密码比单纯的自增编号更接近真实压测负载。② 用 Pipeline 批量写入async function storeUsersInRedis(users) { const pipeline redis.pipeline(); users.forEach(user { if (user) { pipeline.lpush(users, JSON.stringify(user)); } }); await pipeline.exec(); }这里有两个值得注意的设计点使用Pipeline流水线将多条LPUSH命令一次性批量发送大幅减少网络往返RTT这是写入大量数据时的推荐做法用户对象以JSON.stringify序列化后存入列表消费端再用JSON.parse还原——Redis 列表元素是字符串对象必须先序列化。③ 分批主循环for (let i 0; i USERS_COUNT; i BATCH_SIZE) { const users Array.from({ length: BATCH_SIZE }, generateUser); await storeUsersInRedis(users); console.log(Batch ${i / BATCH_SIZE 1} completed.); }全部写入完成后打印All users have been seeded and stored in Redis.并process.exit(0)结束进程。处理器beforeScenario 钩子中 LPOP 唯一用户processor.js 是整个方案的关键。它导出一个异步函数getUser(context, _events)在每个 VU 的场景开始前被调用一次const { Redis } require(upstash/redis); const redis new Redis({ url: process.env.UPSTASH_REDIS_URL, token: process.env.UPSTASH_REDIS_TOKEN }); async function getUser(context, _events) { const initialTime Date.now(); const res await redis.lpop(users, 1); if (res.length 0) { console.error(No users found in Redis); throw new Error(err_no_users_found_in_redis); } context.vars.username res[0].username; context.vars.password res[0].password; const finalTime Date.now(); if (process.env.SHOW_TIMING) { console.log(Time taken: ${finalTime - initialTime}ms); } } module.exports { getUser };逐行解读其设计意图redis.lpop(users, 1)LPOP是原子的弹出并返回操作。1表示一次弹出 1 个元素返回数组元素被弹出后即从列表移除不可能被第二个 VU 再次取到。这正是 Redis 方案解决并发唯一性的核心——不需要分布式锁不需要 worker 间通信一个原子命令即保证全局互斥。空列表防护res.length 0时说明数据池已耗尽此时抛出err_no_users_found_in_redis错误。从 Artillery 源码看场景回调收到错误后会计入vusers.failed见 runner.ts这个显式报错比静默拿到空数据更利于定位问题。写入context.varscontext.vars是 Artillery 每个 VU 独立的上下文变量表后续 flow 步骤通过{{ username }}模板即可引用。若上下文变量注入失效或重复会造成多个 VU 拿到同一账号——而本示例通过LPOP从根上避免了这种可能。SHOW_TIMING开关通过Date.now()差值测量单次拉取耗时便于评估 Redis 调用带来的额外开销。从源码实现看beforeScenario钩子之所以能挂载到每个场景开头是因为 HTTP 引擎在createScenario时会把scenarioSpec.beforeScenario中声明的函数名转成{ function: hookFunctionName }步骤并直接拼接到 flow 的最前面见 engine_http.ts。也就是说beforeScenario本质上就是被注入到场景 flow 首位的处理器函数步骤这就是为什么每个 VU 的场景一启动就会执行getUser。压测脚本极简的场景定义scenario.yml 完整内容如下config: target: http://doesntmatter phases: - duration: 10 arrivalRate: 10 name: Phase 1 processor: ./processor.js scenarios: - beforeScenario: getUser flow: - log: Username: {{ username }} | Password: {{ password }}要点说明target设为http://doesntmatter因为本示例不真正发起 HTTP 请求仅用log步骤打印数据用于直观验证唯一性phases定义 10 秒内以每秒 10 个 VU 的速率持续到达总计约 100 个 VU与播种的 100 个用户正好一一对应——这是刻意设计方便你数一遍日志确认没有重复processor: ./processor.js声明处理器文件。从 runner.ts 的loadProcessor实现可见Artillery 会基于脚本路径解析处理器模块并用import()加载同时兼容 CommonJS 与 ESM加载结果挂回script.config.processorbeforeScenario: getUser引用处理器中导出的函数名。若某个 VU 在getUser中抛错该 VU 场景即失败并计入vusers.failed指标。执行npm run test后控制台会输出约 100 行Username: xxx | Password: xxx每条数据都来自 Redis 中被弹出过的唯一元素。扩展思路播种逻辑并入 before 钩子README 还给出了两条进阶建议将播种步骤并入before钩子当前播种是独立脚本但你完全可以把它写成config.before钩子让 Artillery 在测试启动时自动播种省去手动npm run seed的步骤。before钩子与beforeScenario的区别在于前者在整个测试开始前执行一次由 runner 的handleScriptHook机制驱动见 runner.ts后者在每个 VU 场景开始前执行。需要说明的是在分布式压测场景下before钩子会在每个 worker 上各执行一次因此若多个 worker 同时播种需自行保证幂等例如先DEL users再写入或检查列表长度。也正因如此示例默认把播种放在独立脚本中由你显式控制执行一次。用SHOW_TIMINGtrue测量 Redis 调用开销运行SHOW_TIMINGtrue npm run test处理器会打印每次LPOP的耗时。README 指出Redis 极快通常每次调用只会为每个 VU 增加 30ms的额外耗时具体取决于实例规格、压测规模、网络等因素。你也可以在 processor.js 中调整埋点把耗时作为自定义指标context.stats或scenarioEvents.emit(histogram, ...)上报到 Artillery 汇总报告中量化观察。总结关键点说明问题场景CSV 数据在并发/分布式压测下无法保证 VU 数据唯一解决方案Redis 列表 原子LPOP每个用户只会被弹出一次挂载方式beforeScenario钩子等价于注入 flow 首位的函数步骤数据传递通过context.vars注入flow 用{{ var }}模板引用托管建议自建 RedisAWS ElastiCache或 UpstashServerless推荐开销评估SHOW_TIMINGtrue实测通常 30ms/VU进阶方向播种并入before钩子实现全自动初始化注意分布式幂等这套模式不仅适用于登录凭据还可推广到任何必须唯一的测试数据如订单号、设备 ID、激活码、优惠券——只需调整播种脚本的字段与lpop后的解析逻辑即可。若需深入理解钩子机制的底层实现可继续阅读 engine_http.ts 与 runner.ts若要对比 CSV 方案的上下文注入方式可参考 runner.ts 中datafileVariables的实现。赞分享性能测试接口测试CLI【免费下载链接】artilleryThe complete load testing platform. Everything you need for production-grade load tests. Serverless distributed. Load test with Playwright. Load test HTTP APIs, GraphQL, WebSocket, and more. Use any Node.js module.项目地址https://gitcode.com/gh_mirrors/ar/artillery点击查看免费下载相关推荐Artillery数据驱动测试CSV/Redis数据源集成最佳实践Artillery数据驱动测试CSV/Redis数据源集成最佳实践 引言数据驱动测试的核心价值 在现代应用负载测试中真实场景模拟是确保测试有效性的关键。A性能测试接口测试CLIArtillery 为 VU 动态生成鉴权 Tokenbefore 钩子与 function 步骤实战指南Artillery 为 VU 动态生成鉴权 Tokenbefore 钩子与 function 步骤实战指南 导读 对有状态 API 进行压测时常见需求是先性能测试接口测试CLIJest 全局 API 完全指南describe、test、生命周期钩子与数据驱动测试实战Jest 全局 API 完全指南describe、test、生命周期钩子与数据驱动测试实战 本指南以 Jest 29.7 版本的官方 GlobalAPI.md测试质量保障代码覆盖率开发工具上一篇Kubernetes镜像拉取策略终极防护Kyverno强制配置指南下一篇如何一招解决 vcruntime140.dll 缺失报错VisualCppRedist AIO 离线一键安装指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考