如何在 K6 中实现所有 VU 共享并同步更新的认证 Token

1次阅读

如何在 K6 中实现所有 VU 共享并同步更新的认证 Token

本文介绍在 k6 分布式压测中,如何让所有虚拟用户(vu)共享同一份访问令牌(如 jwt),并在令牌过期前统一刷新——通过 redis 实验模块实现跨 vu 状态协同。

本文介绍在 k6 分布式压测中,如何让所有虚拟用户(vu)共享同一份访问令牌(如 jwt),并在令牌过期前统一刷新——通过 redis 实验模块实现跨 vu 状态协同。

在 K6 中,每个 VU 运行于独立的 JavaScript 沙箱环境中,彼此内存隔离,无法直接共享变量或状态。这意味着 setup() 获取的初始 Token 仅对当前 VU 有效;而当需要全局统一刷新(例如每 55 分钟轮换一次 access token),传统方式(如多 scenario 切换、全局变量赋值或 init context 修改)均无法让已运行的 VU 动态感知新值。

唯一可靠方案:引入外部协调服务 —— Redis

K6 官方提供的实验性模块 k6/x/redis 支持与 Redis 交互,可作为轻量级、高可用的共享状态中心。其核心优势在于:

  • ✅ 单点写入(由专用 VU 更新 token)
  • ✅ 多点读取(所有业务 VU 实时获取最新 token)
  • ✅ 原生支持 GET 阻塞等待(避免轮询开销)
  • ✅ 自动重连与错误恢复(配合合理重试策略)

✅ 推荐架构:双场景协同模式

import { SharedArray } from 'k6/data'; import { sleep } from 'k6'; import redis from 'k6/x/redis';  // 初始化 Redis 客户端(需提前启动 Redis 服务,如 localhost:6379) const redisClient = redis.connect('redis://localhost:6379');  // 场景 1:Token 管理器(单 VU,周期性刷新) export const options = {   scenarios: {     token_manager: {       executor: 'shared-iterations',       vus: 1,       iterations: 100, // 持续运行约 1 小时(按间隔控制)       exec: 'refreshToken',     },     api_test: {       executor: 'constant-vus',       vus: 50,       duration: '1h',       exec: 'makeApiRequest',     },   }, };  // 【场景 1】刷新 Token 并写入 Redis export function refreshToken() {   const token = fetchNewToken(); // 调用你的鉴权接口(含 refresh logic)   if (token) {     redisClient.set('shared_auth_token', token);     console.log(`✅ Token refreshed and published to Redis: ${token.slice(0, 12)}...`);   } else {     console.warn('⚠️  Failed to fetch new token, skipping update.');   }   sleep(3300); // 每 55 分钟刷新一次(预留 5 分钟缓冲) }  // 【场景 2】业务请求(所有 VU 读取共享 Token) export function makeApiRequest() {   let token;   try {     // 阻塞式获取,自动重试直到成功(Redis GET 在 key 不存在时返回 null)     token = redisClient.get('shared_auth_token');     if (!token) {       throw new Error('Token not available in Redis yet');     }   } catch (e) {     console.error('❌ Failed to retrieve token from Redis:', e.message);     // 可选:降级为本地 fallback 或抛出中断     return;   }    // 发起受保护 API 请求   const res = http.post('https://api.example.com/data', JSON.stringify({}), {     headers: {       'Authorization': `Bearer ${token}`,       'Content-Type': 'application/json',     },   });    if (res.status !== 200) {     console.warn(`API request failed: ${res.status} ${res.body}`);   } }  // 示例:模拟获取新 Token 的函数(请替换为你的真实逻辑) function fetchNewToken() {   const authRes = http.post('https://auth.example.com/token/refresh', JSON.stringify({     refresh_token: __ENV.REFRESH_TOKEN || 'your-refresh-token-here',   }), {     headers: { 'Content-Type': 'application/json' },   });    if (authRes.status === 200) {     const json = authRes.json();     return json.access_token || null;   }   return null; }

⚠️ 关键注意事项

  • Redis 必须高可用:建议使用 Redis sentinel 或集群模式,避免单点故障导致全部 VU 阻塞。
  • Token 安全性:不要在日志中打印完整 token(示例中已做 slice 截断),生产环境应禁用敏感字段输出。
  • 错误处理不可省略:redisClient.get() 在连接失败或超时时会抛异常,务必包裹 try/catch。
  • 初始化等待优化:首次运行时,业务 VU 可能因 Redis 中无 token 而阻塞。可在 makeApiRequest 开头添加 while (!token) { sleep(1); token = redisClient.get(…); } 实现主动等待,但需设最大重试次数防死锁。
  • 模块启用要求:运行命令需显式启用实验模块:
    k6 run --experimental-modules script.js

✅ 总结

K6 本身不提供跨 VU 共享状态的能力,但借助 Redis 这一成熟中间件,即可优雅解决「全局 Token 同步刷新」这一典型分布式测试难题。该方案具备低耦合、易扩展、强一致性特点,适用于 OAuth2、JWT、API Key 等多种认证场景。只要确保 Redis 服务稳定,即可支撑数千 VU 规模下的统一身份管理。

text=ZqhQzanResources