257 lines
8.4 KiB
JavaScript
257 lines
8.4 KiB
JavaScript
/**
|
||
* k6 压测脚本 - 场景三:单接口极限压测
|
||
*
|
||
* 用于测试单个接口的极限 QPS/TPS
|
||
* 通过环境变量 API_NAME 指定要测试的接口
|
||
*
|
||
* 支持的 API_NAME 值:
|
||
* - GetPosts : 帖子列表(默认)
|
||
* - GetPostDetail : 帖子详情
|
||
* - GetPostComments : 评论列表
|
||
* - LikePost : 帖子点赞
|
||
* - Rankings : 主播榜单
|
||
* - LiveStreamers : 直播列表
|
||
* - GetUserInfo : 用户信息
|
||
*
|
||
* 运行命令:
|
||
* k6 run --out web-dashboard -e API_NAME=GetPosts scenario-single-api.js
|
||
* k6 run --out web-dashboard -e API_NAME=GetPostDetail -e POST_ID=1 scenario-single-api.js
|
||
*
|
||
* 快速测试:
|
||
* k6 run --out web-dashboard -e QUICK_TEST=true -e API_NAME=GetPosts scenario-single-api.js
|
||
*/
|
||
|
||
import { SINGLE_API_OPTIONS, QUICK_TEST_OPTIONS, TEST_DATA } from './config.js';
|
||
import {
|
||
httpGet,
|
||
httpPost,
|
||
checkResponse,
|
||
parseResponse,
|
||
randomInt,
|
||
getTokenCount,
|
||
API
|
||
} from './utils/helpers.js';
|
||
|
||
// ============================================
|
||
// 压测配置
|
||
// ============================================
|
||
|
||
const isQuickTest = __ENV.QUICK_TEST === 'true';
|
||
const apiName = __ENV.API_NAME || 'GetPosts';
|
||
|
||
// 可选的帖子ID(用于需要帖子ID的接口)
|
||
const envPostId = __ENV.POST_ID ? parseInt(__ENV.POST_ID) : null;
|
||
|
||
export const options = isQuickTest ? QUICK_TEST_OPTIONS : SINGLE_API_OPTIONS;
|
||
|
||
// ============================================
|
||
// 全局变量(setup 阶段获取)
|
||
// ============================================
|
||
|
||
let cachedPostIds = [];
|
||
|
||
// ============================================
|
||
// 初始化阶段
|
||
// ============================================
|
||
|
||
export function setup() {
|
||
const tokenCount = getTokenCount();
|
||
console.log(`========================================`);
|
||
console.log(`场景:单接口极限压测`);
|
||
console.log(`目标接口:${apiName}`);
|
||
console.log(`Token 数量:${tokenCount}`);
|
||
console.log(`模式:${isQuickTest ? '快速测试' : '正式压测'}`);
|
||
console.log(`========================================`);
|
||
|
||
if (tokenCount === 0) {
|
||
throw new Error('没有可用的 Token,请先在 tokens.txt 中添加 Token');
|
||
}
|
||
|
||
// 预先获取帖子ID列表(用于需要帖子ID的接口)
|
||
let postIds = [];
|
||
if (['GetPostDetail', 'GetPostComments', 'LikePost'].includes(apiName)) {
|
||
if (envPostId) {
|
||
postIds = [envPostId];
|
||
console.log(`使用指定的帖子ID: ${envPostId}`);
|
||
} else {
|
||
// 从帖子列表获取
|
||
console.log(`正在获取帖子ID列表...`);
|
||
const res = httpGet(API.POSTS.LIST, {
|
||
SortType: 1,
|
||
PageIndex: 1,
|
||
PageSize: 50, // 获取更多帖子以分散请求
|
||
}, 1);
|
||
|
||
const data = parseResponse(res);
|
||
if (data && data.items && data.items.length > 0) {
|
||
postIds = data.items.map(item => item.postId);
|
||
console.log(`获取到 ${postIds.length} 个帖子ID`);
|
||
} else {
|
||
throw new Error('无法获取帖子列表,请确保数据库中有帖子数据');
|
||
}
|
||
}
|
||
}
|
||
|
||
return { tokenCount, postIds };
|
||
}
|
||
|
||
// ============================================
|
||
// 主测试函数
|
||
// ============================================
|
||
|
||
export default function(data) {
|
||
const vuId = __VU;
|
||
const iteration = __ITER;
|
||
const postIds = data.postIds || [];
|
||
|
||
let res;
|
||
|
||
switch (apiName) {
|
||
// ==================
|
||
// 读接口(GET)
|
||
// ==================
|
||
|
||
case 'GetPosts':
|
||
// 帖子列表 - 随机分页
|
||
res = httpGet(API.POSTS.LIST, {
|
||
SortType: 1,
|
||
PageIndex: randomInt(1, 10),
|
||
PageSize: TEST_DATA.pageSize,
|
||
}, vuId);
|
||
checkResponse(res, 'GetPosts');
|
||
break;
|
||
|
||
case 'GetPostDetail':
|
||
// 帖子详情
|
||
if (postIds.length > 0) {
|
||
const postId = postIds[(vuId + iteration) % postIds.length];
|
||
res = httpGet(API.POSTS.DETAIL, {
|
||
PostId: postId,
|
||
}, vuId);
|
||
checkResponse(res, 'GetPostDetail');
|
||
}
|
||
break;
|
||
|
||
case 'GetPostComments':
|
||
// 评论列表
|
||
if (postIds.length > 0) {
|
||
const postId = postIds[(vuId + iteration) % postIds.length];
|
||
res = httpGet(API.COMMENTS.LIST, {
|
||
PostId: postId,
|
||
SortType: 2,
|
||
PageIndex: randomInt(1, 5),
|
||
PageSize: TEST_DATA.commentPageSize,
|
||
}, vuId);
|
||
checkResponse(res, 'GetPostComments');
|
||
}
|
||
break;
|
||
|
||
case 'Rankings':
|
||
// 主播榜单
|
||
res = httpGet(API.HOME.RANKINGS, {
|
||
category: TEST_DATA.category,
|
||
limit: TEST_DATA.rankingLimit,
|
||
}, vuId);
|
||
checkResponse(res, 'Rankings');
|
||
break;
|
||
|
||
case 'RankingsMore':
|
||
// 更多排行
|
||
res = httpGet(API.HOME.RANKINGS_MORE, {
|
||
category: TEST_DATA.category,
|
||
page: randomInt(1, 5),
|
||
pageSize: 20,
|
||
}, vuId);
|
||
checkResponse(res, 'RankingsMore');
|
||
break;
|
||
|
||
case 'LiveStreamers':
|
||
// 直播列表
|
||
res = httpGet(API.HOME.LIVE_STREAMERS, {
|
||
page: randomInt(1, 5),
|
||
pageSize: 10,
|
||
}, vuId);
|
||
checkResponse(res, 'LiveStreamers');
|
||
break;
|
||
|
||
case 'GetUserInfo':
|
||
// 用户信息
|
||
res = httpGet(API.USER.INFO, {}, vuId);
|
||
checkResponse(res, 'GetUserInfo');
|
||
break;
|
||
|
||
case 'Banners':
|
||
// 轮播图
|
||
res = httpGet(API.HOME.BANNERS, {}, vuId);
|
||
checkResponse(res, 'Banners');
|
||
break;
|
||
|
||
case 'GetMessages':
|
||
// 消息列表
|
||
res = httpGet(API.MESSAGES.LIST, {
|
||
MessageType: 3,
|
||
PageIndex: 1,
|
||
PageSize: 10,
|
||
}, vuId);
|
||
checkResponse(res, 'GetMessages');
|
||
break;
|
||
|
||
// ==================
|
||
// 写接口(POST)
|
||
// ==================
|
||
|
||
case 'LikePost':
|
||
// 帖子点赞
|
||
if (postIds.length > 0) {
|
||
const postId = postIds[(vuId + iteration) % postIds.length];
|
||
// 交替点赞/取消,避免状态冲突
|
||
const action = (iteration % 2 === 0) ? 1 : 2;
|
||
res = httpPost(API.POSTS.LIKE, {
|
||
postId: postId,
|
||
action: action,
|
||
}, vuId);
|
||
checkResponse(res, 'LikePost');
|
||
}
|
||
break;
|
||
|
||
case 'LikeComment':
|
||
// 评论点赞(需要先获取评论ID)
|
||
if (postIds.length > 0) {
|
||
const postId = postIds[(vuId + iteration) % postIds.length];
|
||
// 先获取评论
|
||
const commentsRes = httpGet(API.COMMENTS.LIST, {
|
||
PostId: postId,
|
||
SortType: 2,
|
||
PageIndex: 1,
|
||
PageSize: 10,
|
||
}, vuId);
|
||
const commentsData = parseResponse(commentsRes);
|
||
if (commentsData && commentsData.items && commentsData.items.length > 0) {
|
||
const commentId = commentsData.items[0].commentId;
|
||
const action = (iteration % 2 === 0) ? 1 : 2;
|
||
res = httpPost(API.COMMENTS.LIKE, {
|
||
commentId: commentId,
|
||
action: action,
|
||
}, vuId);
|
||
checkResponse(res, 'LikeComment');
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
console.log(`未知的 API_NAME: ${apiName}`);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// ============================================
|
||
// 结束阶段
|
||
// ============================================
|
||
|
||
export function teardown(data) {
|
||
console.log(`========================================`);
|
||
console.log(`单接口压测结束`);
|
||
console.log(`接口:${apiName}`);
|
||
console.log(`========================================`);
|
||
}
|