265 lines
6.9 KiB
JavaScript
265 lines
6.9 KiB
JavaScript
/**
|
||
* k6 压测工具函数
|
||
* 直播论坛项目
|
||
*/
|
||
|
||
import http from 'k6/http';
|
||
import { check, sleep } from 'k6';
|
||
import { SharedArray } from 'k6/data';
|
||
import { BASE_URL, HTTP_TIMEOUT, THINK_TIME } from '../config.js';
|
||
|
||
// ============================================
|
||
// Token 管理
|
||
// ============================================
|
||
|
||
// 使用 SharedArray 加载 Token(所有 VU 共享,节省内存)
|
||
const tokens = new SharedArray('tokens', function() {
|
||
// 读取 tokens.txt 文件
|
||
const data = open('../tokens.txt');
|
||
// 按行分割,过滤空行和注释行
|
||
return data.split('\n')
|
||
.map(line => line.trim())
|
||
.filter(line => line && !line.startsWith('#'));
|
||
});
|
||
|
||
/**
|
||
* 获取 Token 总数
|
||
*/
|
||
export function getTokenCount() {
|
||
return tokens.length;
|
||
}
|
||
|
||
/**
|
||
* 根据 VU 编号获取 Token
|
||
* 如果 VU 数量超过 Token 数量,会循环使用
|
||
* @param {number} vuId - VU 编号 (__VU)
|
||
* @returns {string} Token(已添加 Bearer 前缀)
|
||
*/
|
||
export function getToken(vuId) {
|
||
if (tokens.length === 0) {
|
||
console.error('警告: tokens.txt 中没有有效的 Token!');
|
||
return '';
|
||
}
|
||
const index = (vuId - 1) % tokens.length;
|
||
const token = tokens[index];
|
||
return `Bearer ${token}`;
|
||
}
|
||
|
||
/**
|
||
* 获取请求头(带认证)
|
||
* @param {number} vuId - VU 编号
|
||
* @returns {object} HTTP Headers
|
||
*/
|
||
export function getHeaders(vuId) {
|
||
return {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': getToken(vuId),
|
||
};
|
||
}
|
||
|
||
// ============================================
|
||
// HTTP 请求封装
|
||
// ============================================
|
||
|
||
/**
|
||
* 发送 GET 请求
|
||
* @param {string} path - API 路径
|
||
* @param {object} params - URL 参数
|
||
* @param {number} vuId - VU 编号
|
||
* @returns {object} HTTP Response
|
||
*/
|
||
export function httpGet(path, params = {}, vuId) {
|
||
const url = buildUrl(path, params);
|
||
const response = http.get(url, {
|
||
headers: getHeaders(vuId),
|
||
timeout: HTTP_TIMEOUT,
|
||
tags: { api: path.split('?')[0] },
|
||
});
|
||
return response;
|
||
}
|
||
|
||
/**
|
||
* 发送 POST 请求
|
||
* @param {string} path - API 路径
|
||
* @param {object} body - 请求体
|
||
* @param {number} vuId - VU 编号
|
||
* @returns {object} HTTP Response
|
||
*/
|
||
export function httpPost(path, body = {}, vuId) {
|
||
const url = `${BASE_URL}${path}`;
|
||
const response = http.post(url, JSON.stringify(body), {
|
||
headers: getHeaders(vuId),
|
||
timeout: HTTP_TIMEOUT,
|
||
tags: { api: path },
|
||
});
|
||
return response;
|
||
}
|
||
|
||
/**
|
||
* 构建带参数的 URL
|
||
* @param {string} path - API 路径
|
||
* @param {object} params - URL 参数
|
||
* @returns {string} 完整 URL
|
||
*/
|
||
function buildUrl(path, params) {
|
||
let url = `${BASE_URL}${path}`;
|
||
const queryString = Object.entries(params)
|
||
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
||
.join('&');
|
||
if (queryString) {
|
||
url += (path.includes('?') ? '&' : '?') + queryString;
|
||
}
|
||
return url;
|
||
}
|
||
|
||
// ============================================
|
||
// 响应检查
|
||
// ============================================
|
||
|
||
/**
|
||
* 检查响应是否成功
|
||
* @param {object} response - HTTP Response
|
||
* @param {string} name - 检查名称
|
||
* @returns {boolean} 是否成功
|
||
*/
|
||
export function checkResponse(response, name) {
|
||
const result = check(response, {
|
||
[`${name} - status is 200`]: (r) => r.status === 200,
|
||
[`${name} - code is 0`]: (r) => {
|
||
try {
|
||
const body = JSON.parse(r.body);
|
||
return body.code === 0;
|
||
} catch {
|
||
return false;
|
||
}
|
||
},
|
||
});
|
||
|
||
// 如果失败,打印详细信息(仅在调试时有用)
|
||
if (!result && response.status !== 200) {
|
||
console.log(`${name} 失败: status=${response.status}, body=${response.body.substring(0, 200)}`);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 解析响应体
|
||
* @param {object} response - HTTP Response
|
||
* @returns {object|null} 解析后的数据
|
||
*/
|
||
export function parseResponse(response) {
|
||
try {
|
||
const body = JSON.parse(response.body);
|
||
if (body.code === 0) {
|
||
return body.data;
|
||
}
|
||
return null;
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// ============================================
|
||
// 模拟用户行为
|
||
// ============================================
|
||
|
||
/**
|
||
* 模拟用户思考时间(随机等待)
|
||
*/
|
||
export function thinkTime() {
|
||
const duration = Math.random() * (THINK_TIME.max - THINK_TIME.min) + THINK_TIME.min;
|
||
sleep(duration);
|
||
}
|
||
|
||
/**
|
||
* 短暂等待(API 调用间隔)
|
||
*/
|
||
export function shortWait() {
|
||
sleep(0.5 + Math.random() * 0.5);
|
||
}
|
||
|
||
// ============================================
|
||
// 数据生成
|
||
// ============================================
|
||
|
||
/**
|
||
* 生成随机评论内容
|
||
* @returns {string} 评论内容
|
||
*/
|
||
export function randomComment() {
|
||
const comments = [
|
||
'压测评论内容_' + Date.now(),
|
||
'这是一条测试评论_' + Math.random().toString(36).substring(7),
|
||
'好帖子!' + Date.now(),
|
||
'支持一下~' + Math.random().toString(36).substring(7),
|
||
'学习了!' + Date.now(),
|
||
];
|
||
return comments[Math.floor(Math.random() * comments.length)];
|
||
}
|
||
|
||
/**
|
||
* 生成随机整数
|
||
* @param {number} min - 最小值
|
||
* @param {number} max - 最大值
|
||
* @returns {number} 随机整数
|
||
*/
|
||
export function randomInt(min, max) {
|
||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||
}
|
||
|
||
// ============================================
|
||
// API 路径常量
|
||
// ============================================
|
||
|
||
export const API = {
|
||
// 认证
|
||
AUTH: {
|
||
LOGIN: '/api/Auth/WechatMpLogin',
|
||
REFRESH: '/api/Auth/RefreshToken',
|
||
},
|
||
// 首页
|
||
HOME: {
|
||
BANNERS: '/api/Banners/GetBanners',
|
||
CATEGORIES: '/api/Home/StreamerCategories',
|
||
RANKINGS: '/api/Home/Rankings',
|
||
RANKINGS_MORE: '/api/Home/RankingsMore',
|
||
LIVE_STREAMERS: '/api/Home/LiveStreamers',
|
||
},
|
||
// 帖子
|
||
POSTS: {
|
||
LIST: '/api/Posts/GetPosts',
|
||
DETAIL: '/api/Posts/GetPostDetail',
|
||
PUBLISH: '/api/Posts/PublishPosts',
|
||
LIKE: '/api/Posts/LikePost',
|
||
DELETE: '/api/Posts/DeletePosts',
|
||
MY_POSTS: '/api/Posts/GetMyPosts',
|
||
},
|
||
// 评论
|
||
COMMENTS: {
|
||
LIST: '/api/PostComments/GetPostComments',
|
||
PUBLISH: '/api/PostComments/PublishPostComments',
|
||
REPLIES: '/api/PostComments/GetCommentsReplies',
|
||
LIKE: '/api/PostComments/LikeComment',
|
||
},
|
||
// 送花
|
||
FLOWERS: {
|
||
SEND: '/api/Flowers/SendFlower',
|
||
},
|
||
// 用户
|
||
USER: {
|
||
INFO: '/api/UsersInfo/GetUserInfo',
|
||
UPDATE: '/api/UsersInfo/UpdateUserInfo',
|
||
},
|
||
// 消息
|
||
MESSAGES: {
|
||
LIST: '/api/Messages/GetMessages',
|
||
PARTICIPATE: '/api/Messages/ParticipateActivity',
|
||
},
|
||
// 配置
|
||
CONFIG: {
|
||
APP: '/api/Config/GetAppConfig',
|
||
AGREEMENT: '/api/Config/GetAgreement',
|
||
},
|
||
};
|