
本文介绍在 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 规模下的统一身份管理。










