// Sub2API Mixed Workload Performance Test // 综合负载性能测试 - 模拟真实用户行为 import http from 'k6/http'; import { check, sleep, group } from 'k6'; import { Rate, Trend, Counter } from 'k6/metrics'; import { config, getBaseUrl, getAuthToken } from '../common/utils.js'; import { httpGet, httpPost, randomSleep, randomChoice } from '../common/utils.js'; // ============= 自定义指标 ============= const totalRequestDuration = new Trend('total_request_duration'); const errorRate = new Rate('errors'); const throughputCounter = new Counter('throughput'); // 分模块指标 const moduleMetrics = { health: new Trend('health_duration'), auth: new Trend('auth_duration'), apiKeys: new Trend('apikeys_duration'), gateway: new Trend('gateway_duration'), admin: new Trend('admin_duration'), }; // ============= 测试配置 ============= export const options = { scenarios: { baseline: { executor: 'ramping-vus', startVUs: 10, stages: [ { duration: '2m', target: 10 }, // 预热 { duration: '3m', target: 50 }, // 正常负载 { duration: '1m', target: 0 }, // 冷却 ], }, load: { executor: 'ramping-vus', startVUs: 20, stages: [ { duration: '2m', target: 20 }, // 预热 { duration: '2m', target: 100 }, // 正常负载 { duration: '2m', target: 200 }, // 峰值负载 { duration: '2m', target: 200 }, // 持续峰值 { duration: '2m', target: 0 }, // 冷却 ], }, stress: { executor: 'ramping-vus', startVUs: 50, stages: [ { duration: '1m', target: 50 }, // 预热 { duration: '2m', target: 200 }, // 正常负载 { duration: '2m', target: 500 }, // 高负载 { duration: '3m', target: 1000 }, // 极限负载 { duration: '2m', target: 0 }, // 冷却 ], }, soak: { executor: 'constant-vus', vus: 100, duration: '8h', }, spike: { executor: 'ramping-vus', startVUs: 50, stages: [ { duration: '30s', target: 50 }, // 基线 { duration: '1m', target: 1000 }, // 尖峰 { duration: '1m', target: 1000 }, // 保持尖峰 { duration: '30s', target: 50 }, // 恢复 { duration: '2m', target: 0 }, // 冷却 ], }, }, thresholds: { // 全局阈值 'errors': ['rate<0.02'], // 错误率 < 2% 'total_request_duration': ['p(95)<2000', 'p(99)<5000'], // 各模块阈值 'health_duration': ['p(95)<200'], 'auth_duration': ['p(95)<500'], 'apikeys_duration': ['p(95)<1000'], 'gateway_duration': ['p(95)<3000'], 'admin_duration': ['p(95)<1500'], }, }; // ============= 辅助函数 ============= function getHeaders() { return { 'Authorization': `Bearer ${getAuthToken()}`, 'Content-Type': 'application/json', }; } // ============= 测试场景 ============= /** * 健康检查测试 */ function testHealthCheck() { const start = Date.now(); const res = http.get(`${getBaseUrl()}/health`, { tags: { name: 'health' }, }); moduleMetrics.health.add(Date.now() - start); errorRate.add(res.status !== 200); check(res, { 'Health check OK': (r) => r.status === 200, }); } /** * 认证测试 */ function testAuth() { const start = Date.now(); // 登录 const loginRes = http.post( `${getBaseUrl()}/api/v1/auth/login`, JSON.stringify({ email: 'user@example.com', password: 'password123', }), { headers: { 'Content-Type': 'application/json' }, tags: { name: 'auth_login' }, } ); moduleMetrics.auth.add(Date.now() - start); errorRate.add(loginRes.status !== 200); check(loginRes, { 'Login OK': (r) => r.status === 200, 'Has token': (r) => r.json('token') !== undefined, }); } /** * API Key 管理测试 */ function testAPIKeys() { const start = Date.now(); // 列出 keys const listRes = http.get(`${getBaseUrl()}/api/v1/keys`, { headers: getHeaders(), tags: { name: 'apikeys_list' }, }); check(listRes, { 'List OK': (r) => r.status === 200, }); // 随机选择一个操作 const op = randomChoice(['create', 'update', 'delete']); if (listRes.status === 200) { const keys = listRes.json('data'); if (op === 'create' && keys.length < 10) { // 创建新 key const createRes = http.post( `${getBaseUrl()}/api/v1/keys`, JSON.stringify({ name: `perf-test-${Date.now()}` }), { headers: getHeaders(), tags: { name: 'apikeys_create' } } ); check(createRes, { 'Create OK': (r) => r.status === 201, }); } else if (keys.length > 0) { // 更新或删除 const keyId = keys[0].id; if (op === 'update') { const updateRes = http.patch( `${getBaseUrl()}/api/v1/keys/${keyId}`, JSON.stringify({ name: `updated-${Date.now()}` }), { headers: getHeaders(), tags: { name: 'apikeys_update' } } ); check(updateRes, { 'Update OK': (r) => r.status === 200, }); } // delete 操作可选执行,避免清理测试数据 } } moduleMetrics.apikeys.add(Date.now() - start); } /** * Gateway API 测试 */ function testGateway() { const start = Date.now(); // 准备 API Key const listRes = http.get(`${getBaseUrl()}/api/v1/keys`, { headers: getHeaders(), }); if (listRes.status !== 200) { return; } const keys = listRes.json('data'); if (!keys || keys.length === 0) { return; } const apiKey = keys[0].key; // 随机选择一个平台 const platform = randomChoice(['openai', 'claude', 'gemini']); let res; let model; switch (platform) { case 'openai': res = http.post( `${getBaseUrl()}/v1/chat/completions`, JSON.stringify({ model: 'gpt-3.5-turbo', messages: [{ role: 'user', content: 'Hi' }], max_tokens: 10, }), { headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, tags: { name: 'gateway_openai' }, } ); break; case 'claude': res = http.post( `${getBaseUrl()}/v1/messages`, JSON.stringify({ model: 'claude-3-5-haiku-20241022', messages: [{ role: 'user', content: 'Hi' }], max_tokens: 10, }), { headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', 'anthropic-version': '2023-06-01', }, tags: { name: 'gateway_claude' }, } ); break; case 'gemini': res = http.post( `${getBaseUrl()}/v1beta/models/gemini-pro:generateContent`, JSON.stringify({ contents: [{ parts: [{ text: 'Hi' }] }], }), { headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, tags: { name: 'gateway_gemini' }, } ); break; } moduleMetrics.gateway.add(Date.now() - start); // Gateway 错误不计入全局错误率(上游可能不可用) check(res, { 'Gateway OK or expected error': (r) => r.status < 500, }); } /** * Admin API 测试(仅部分 VU) */ function testAdmin() { if (__VU % 10 !== 0) { return; // 只有 10% 的 VU 执行 admin 测试 } const start = Date.now(); const res = http.get(`${getBaseUrl()}/api/v1/admin/dashboard`, { headers: getHeaders(), tags: { name: 'admin_dashboard' }, }); moduleMetrics.admin.add(Date.now() - start); check(res, { 'Admin OK': (r) => r.status === 200, }); } // ============= 用户行为模拟 ============= /** * 模拟真实用户行为 */ function simulateUserBehavior() { // 根据权重选择操作 const rand = Math.random(); if (rand < 0.05) { // 5% 健康检查 testHealthCheck(); } else if (rand < 0.10) { // 5% 登录 testAuth(); } else if (rand < 0.25) { // 15% API Key 管理 testAPIKeys(); } else if (rand < 0.95) { // 70% Gateway 请求 testGateway(); } else { // 5% Admin 请求 testAdmin(); } } // ============= 主测试函数 ============= export default function () { const start = Date.now(); group('Mixed Workload', () => { simulateUserBehavior(); }); totalRequestDuration.add(Date.now() - start); throughputCounter.add(1); // 随机思考时间 randomSleep(0.5, 3); } // ============= 测试报告 ============= export function handleSummary(data) { const metrics = data.metrics; return { 'mixed-workload-report.json': JSON.stringify(data, null, 2), 'mixed-workload-summary.txt': ` ================================================================================ Sub2API 综合负载性能测试报告 ================================================================================ 测试时间: ${new Date().toISOString()} 测试持续: ${(data.state.testRunDurationMs / 1000 / 60).toFixed(2)} 分钟 峰值 VU: ${metrics?.vus?.peak || 0} 最终 VU: ${metrics?.vus?.value || 0} -------------------------------------------------------------------------------- 核心指标 -------------------------------------------------------------------------------- 总请求数: ${metrics?.throughput?.values?.count || 0} 错误率: ${((metrics?.errors?.values?.rate || 0) * 100).toFixed(2)}% 平均响应时间: ${metrics?.total_request_duration?.values?.avg?.toFixed(2) || 0} ms P50 响应时间: ${metrics?.total_request_duration?.values?.['p(50)']?.toFixed(2) || 0} ms P95 响应时间: ${metrics?.total_request_duration?.values?.['p(95)']?.toFixed(2) || 0} ms P99 响应时间: ${metrics?.total_request_duration?.values?.['p(99)']?.toFixed(2) || 0} ms 最大响应时间: ${metrics?.total_request_duration?.values?.max?.toFixed(2) || 0} ms -------------------------------------------------------------------------------- 分模块性能 -------------------------------------------------------------------------------- 健康检查: 平均: ${metrics?.health_duration?.values?.avg?.toFixed(2) || 0} ms P95: ${metrics?.health_duration?.values?.['p(95)']?.toFixed(2) || 0} ms 认证: 平均: ${metrics?.auth_duration?.values?.avg?.toFixed(2) || 0} ms P95: ${metrics?.auth_duration?.values?.['p(95)']?.toFixed(2) || 0} ms API Key 管理: 平均: ${metrics?.apikeys_duration?.values?.avg?.toFixed(2) || 0} ms P95: ${metrics?.apikeys_duration?.values?.['p(95)']?.toFixed(2) || 0} ms Gateway: 平均: ${metrics?.gateway_duration?.values?.avg?.toFixed(2) || 0} ms P95: ${metrics?.gateway_duration?.values?.['p(95)']?.toFixed(2) || 0} ms P99: ${metrics?.gateway_duration?.values?.['p(99)']?.toFixed(2) || 0} ms 管理后台: 平均: ${metrics?.admin_duration?.values?.avg?.toFixed(2) || 0} ms P95: ${metrics?.admin_duration?.values?.['p(95)']?.toFixed(2) || 0} ms ================================================================================ 测试完成 ================================================================================ `, }; }