// Sub2API API Key Management Performance Test // API Key 管理接口性能测试 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, generateTestAPIKeyName } from '../common/utils.js'; // ============= 自定义指标 ============= const apiKeyListDuration = new Trend('apikey_list_duration'); const apiKeyCreateDuration = new Trend('apikey_create_duration'); const apiKeyUpdateDuration = new Trend('apikey_update_duration'); const apiKeyDeleteDuration = new Trend('apikey_delete_duration'); const apiKeyErrorRate = new Rate('apikey_errors'); // ============= 测试配置 ============= export const options = { scenarios: { apikey_load: { executor: 'ramping-vus', startVUs: 5, stages: [ { duration: '1m', target: 20 }, // 预热 { duration: '3m', target: 50 }, // 正常负载 { duration: '2m', target: 100 }, // 峰值负载 { duration: '1m', target: 0 }, // 冷却 ], }, }, thresholds: { 'apikey_list_duration': ['p(95)<500', 'p(99)<1000'], 'apikey_create_duration': ['p(95)<1000', 'p(99)<2000'], 'apikey_errors': ['rate<0.01'], }, }; // ============= 辅助函数 ============= /** * 获取认证头 */ function getHeaders() { return { 'Authorization': `Bearer ${getAuthToken()}`, 'Content-Type': 'application/json', }; } /** * 创建测试用 API Key */ function createTestAPIKey(name) { const res = http.post( `${getBaseUrl()}/api/v1/keys`, JSON.stringify({ name: name || generateTestAPIKeyName() }), { headers: getHeaders(), tags: { name: 'apikey_create' } } ); apiKeyCreateDuration.add(res.timings.duration); return res; } /** * 列出 API Keys */ function listAPIKeys(params = {}) { const url = `${getBaseUrl()}/api/v1/keys${params.page ? `?page=${params.page}` : ''}`; const res = http.get(url, { headers: getHeaders(), tags: { name: 'apikey_list' } }); apiKeyListDuration.add(res.timings.duration); return res; } /** * 获取单个 API Key 详情 */ function getAPIKey(id) { const res = http.get(`${getBaseUrl()}/api/v1/keys/${id}`, { headers: getHeaders(), tags: { name: 'apikey_get' }, }); return res; } /** * 更新 API Key */ function updateAPIKey(id, updates) { const res = http.patch(`${getBaseUrl()}/api/v1/keys/${id}`, JSON.stringify(updates), { headers: getHeaders(), tags: { name: 'apikey_update' }, }); apiKeyUpdateDuration.add(res.timings.duration); return res; } /** * 删除 API Key */ function deleteAPIKey(id) { const res = http.delete(`${getBaseUrl()}/api/v1/keys/${id}`, { headers: getHeaders(), tags: { name: 'apikey_delete' }, }); apiKeyDeleteDuration.add(res.timings.duration); return res; } // ============= 测试场景 ============= /** * 测试 CRUD 完整流程 */ function testCRUDFlow() { // 1. 创建 const createRes = createTestAPIKey(`perf-crud-${Date.now()}`); check(createRes, { 'Create: status is 201': (r) => r.status === 201, 'Create: has key data': (r) => r.json('id') !== undefined, }); apiKeyErrorRate.add(createRes.status !== 201); if (createRes.status !== 201) return null; const keyId = createRes.json('id'); // 2. 读取 const getRes = getAPIKey(keyId); check(getRes, { 'Get: status is 200': (r) => r.status === 200, 'Get: id matches': (r) => r.json('id') === keyId, }); // 3. 更新 const updateRes = updateAPIKey(keyId, { name: `updated-${Date.now()}` }); check(updateRes, { 'Update: status is 200': (r) => r.status === 200, 'Update: name changed': (r) => r.json('name').includes('updated'), }); apiKeyErrorRate.add(updateRes.status !== 200); // 4. 删除 const deleteRes = deleteAPIKey(keyId); check(deleteRes, { 'Delete: status is 204': (r) => r.status === 204, }); apiKeyErrorRate.add(deleteRes.status !== 204); return keyId; } /** * 测试列表查询 */ function testListOperations() { // 测试分页 const page1 = listAPIKeys({ page: 1 }); check(page1, { 'List Page 1: status is 200': (r) => r.status === 200, 'List Page 1: has data': (r) => Array.isArray(r.json('data')), }); apiKeyErrorRate.add(page1.status !== 200); // 测试搜索 const searchRes = http.get(`${getBaseUrl()}/api/v1/keys?search=perf`, { headers: getHeaders(), tags: { name: 'apikey_search' }, }); check(searchRes, { 'Search: status is 200': (r) => r.status === 200, }); // 随机睡眠模拟用户浏览 randomSleep(0.1, 0.5); } /** * 测试并发创建 */ function testConcurrentCreate() { // 批量创建请求 const batch = []; const count = 5; for (let i = 0; i < count; i++) { batch.push([ 'POST', `${getBaseUrl()}/api/v1/keys`, JSON.stringify({ name: `batch-${Date.now()}-${i}` }), { headers: getHeaders(), tags: { name: 'apikey_batch_create' }, }, ]); } const responses = http.batch(batch); let successCount = 0; for (const res of responses) { if (res.status === 201) successCount++; apiKeyErrorRate.add(res.status !== 201); } check(responses[0], { 'Batch Create: at least 80% success': () => successCount / count >= 0.8, }); return successCount; } // ============= 主测试函数 ============= export default function () { const vuId = __VU; const iteration = __ITER__; group('API Key Management', () => { // 每个 VU 的操作模式 const mode = vuId % 4; switch (mode) { case 0: // 读为主:列表 + 单个查询 listAPIKeys(); randomSleep(0.2, 0.5); // 获取一个 key 进行查询 const listRes = listAPIKeys(); if (listRes.status === 200 && listRes.json('data').length > 0) { const keyId = listRes.json('data')[0].id; getAPIKey(keyId); } break; case 1: // 写为主:创建 + 更新 testCRUDFlow(); break; case 2: // 混合操作 listAPIKeys(); randomSleep(0.1, 0.3); testListOperations(); break; case 3: // 批量操作 if (iteration % 10 === 0) { testConcurrentCreate(); } else { listAPIKeys(); } break; } }); // 全局思考时间 randomSleep(0.5, 2); } // ============= 测试报告 ============= export function handleSummary(data) { return { 'apikey-performance-report.json': JSON.stringify(data, null, 2), 'apikey-performance-summary.txt': generateSummary(data), }; } function generateSummary(data) { const metrics = data.metrics; return ` ================================================================================ Sub2API API Key Management 性能测试报告 ================================================================================ 测试时间: ${new Date().toISOString()} 测试持续: ${(data.state.testRunDurationMs / 1000 / 60).toFixed(2)} 分钟 -------------------------------------------------------------------------------- 核心指标 -------------------------------------------------------------------------------- 总请求数: ${metrics?.requests_total?.values?.count || 0} 错误率: ${((metrics?.apikey_errors?.values?.rate || 0) * 100).toFixed(2)}% -------------------------------------------------------------------------------- 操作类型性能 -------------------------------------------------------------------------------- 列表操作: 平均响应: ${metrics?.apikey_list_duration?.values?.avg?.toFixed(2) || 0} ms P95 响应: ${metrics?.apikey_list_duration?.values?.['p(95)']?.toFixed(2) || 0} ms P99 响应: ${metrics?.apikey_list_duration?.values?.['p(99)']?.toFixed(2) || 0} ms 创建操作: 平均响应: ${metrics?.apikey_create_duration?.values?.avg?.toFixed(2) || 0} ms P95 响应: ${metrics?.apikey_create_duration?.values?.['p(95)']?.toFixed(2) || 0} ms P99 响应: ${metrics?.apikey_create_duration?.values?.['p(99)']?.toFixed(2) || 0} ms 更新操作: 平均响应: ${metrics?.apikey_update_duration?.values?.avg?.toFixed(2) || 0} ms P95 响应: ${metrics?.apikey_update_duration?.values?.['p(95)']?.toFixed(2) || 0} ms 删除操作: 平均响应: ${metrics?.apikey_delete_duration?.values?.avg?.toFixed(2) || 0} ms P95 响应: ${metrics?.apikey_delete_duration?.values?.['p(95)']?.toFixed(2) || 0} ms ================================================================================ 测试完成 ================================================================================ `; }