mirror of
https://github.com/ZeroCatDev/ClassworksKV.git
synced 2025-07-01 11:59:22 +00:00
Refactor project structure by reorganizing imports, removing unused files, and updating .gitignore
to include additional environment and build directories. Delete legacy migration scripts and Docker configurations to streamline the codebase.
This commit is contained in:
parent
0a8cd084d6
commit
35d05df925
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@ -5,8 +5,8 @@
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "" # See documentation for possible values
|
||||
- package-ecosystem: "npm" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
|
||||
|
147
.gitignore
vendored
147
.gitignore
vendored
@ -1,4 +1,147 @@
|
||||
node_modules
|
||||
# Keep environment variables out of version control
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node
|
||||
|
||||
prisma/database/data
|
||||
data/
|
||||
|
2
app.js
2
app.js
@ -1,4 +1,4 @@
|
||||
import "./instrumentation.js";
|
||||
import "./utils/instrumentation.js";
|
||||
// import createError from "http-errors";
|
||||
import express from "express";
|
||||
import { join, dirname } from "path";
|
||||
|
BIN
data/db.db
BIN
data/db.db
Binary file not shown.
@ -1,5 +1,4 @@
|
||||
import { siteKey } from "../config.js";
|
||||
import AppError from "../utils/errors.js";
|
||||
import { siteKey } from "../utils/config.js";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { DecodeAndVerifyPassword, verifySiteKey } from "../utils/crypto.js";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { isDevelopment } from "../config.js";
|
||||
import { isDevelopment } from "../utils/config.js";
|
||||
|
||||
const errorHandler = (err, req, res, next) => {
|
||||
// 判断响应是否已经发送
|
||||
|
@ -7,10 +7,7 @@
|
||||
"prisma": "prisma generate",
|
||||
"prisma:pull": "prisma db pull",
|
||||
"dev": "NODE_ENV=development nodemon node .bin/www",
|
||||
"test:rate-limit": "node ./scripts/test-rate-limit.js",
|
||||
"test:stress": "node ./scripts/stress-test.js",
|
||||
"test:distributed": "node ./scripts/distributed-test.js",
|
||||
"test:all-limits": "node ./scripts/run-all-tests.js"
|
||||
"migrate": "node ./scripts/batchMigrate.js"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Router } from "express";
|
||||
const router = Router();
|
||||
import kvStore from "../models/kvStore.js";
|
||||
import kvStore from "../utils/kvStore.js";
|
||||
import { checkSiteKey } from "../middleware/auth.js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import errors from "../utils/errors.js";
|
||||
|
@ -1,75 +0,0 @@
|
||||
# 接口限速测试脚本
|
||||
|
||||
这个目录包含了用于测试API接口限速功能的脚本。
|
||||
|
||||
## 前置条件
|
||||
|
||||
在运行测试脚本之前,请确保安装了所需的依赖:
|
||||
|
||||
```bash
|
||||
npm install axios
|
||||
```
|
||||
|
||||
## 可用测试脚本
|
||||
|
||||
### 1. 功能测试 (test-rate-limit.js)
|
||||
|
||||
测试不同类型的限速功能是否正常工作,包括全局限速、API限速、写操作限速和删除操作限速。
|
||||
|
||||
```bash
|
||||
npm run test:rate-limit
|
||||
# 或直接运行
|
||||
node scripts/test-rate-limit.js
|
||||
```
|
||||
|
||||
### 2. 压力测试 (stress-test.js)
|
||||
|
||||
对指定端点进行高并发请求,测试限速在高负载下的表现。
|
||||
|
||||
```bash
|
||||
npm run test:stress
|
||||
# 或直接运行
|
||||
node scripts/stress-test.js
|
||||
```
|
||||
|
||||
### 3. 分布式测试 (distributed-test.js)
|
||||
|
||||
模拟多个不同IP地址的请求,测试基于IP的限速是否有效。
|
||||
|
||||
```bash
|
||||
npm run test:distributed
|
||||
# 或直接运行
|
||||
node scripts/distributed-test.js
|
||||
```
|
||||
|
||||
### 4. 运行所有测试 (run-all-tests.js)
|
||||
|
||||
按顺序运行所有测试,并在测试之间添加适当的延迟以重置限速计数器。
|
||||
|
||||
```bash
|
||||
npm run test:all-limits
|
||||
# 或直接运行
|
||||
node scripts/run-all-tests.js
|
||||
```
|
||||
|
||||
## 配置测试参数
|
||||
|
||||
每个测试脚本的开头都有配置参数,可以根据需要进行调整:
|
||||
|
||||
- `BASE_URL`: API服务器的基础URL(默认为 http://localhost:3000)
|
||||
- `CONCURRENT_REQUESTS`: 并发请求数(仅适用于压力测试)
|
||||
- `TOTAL_REQUESTS`: 总请求数
|
||||
- `SIMULATED_IPS`: 模拟的IP数量(仅适用于分布式测试)
|
||||
- `REQUESTS_PER_IP`: 每个IP的请求数(仅适用于分布式测试)
|
||||
- `TEST_ENDPOINT`: 测试的API端点
|
||||
|
||||
## 测试结果说明
|
||||
|
||||
测试脚本会输出彩色的测试进度和结果统计信息:
|
||||
|
||||
- 绿色点(`.`): 成功的请求
|
||||
- 黄色L(`L`): 被限速的请求
|
||||
- 红色E(`E`): 错误的请求
|
||||
- 红色X(`X`): 请求异常
|
||||
|
||||
测试完成后,会显示总体统计信息,包括总请求数、成功请求数、被限速请求数、错误请求数和限速比例。
|
@ -1,182 +0,0 @@
|
||||
import axios from 'axios';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
// 配置
|
||||
const BASE_URL = 'http://localhost:3000'; // 修改为你的服务器地址和端口
|
||||
const TEST_NAMESPACE = uuidv4(); // 生成随机命名空间用于测试
|
||||
const SIMULATED_IPS = 10; // 模拟的IP数量
|
||||
const REQUESTS_PER_IP = 30; // 每个IP的请求数
|
||||
const TEST_ENDPOINT = '/check'; // 测试端点
|
||||
const DELAY_BETWEEN_BATCHES = 500; // 批次间延迟(毫秒)
|
||||
|
||||
// 测试结果统计
|
||||
const stats = {
|
||||
totalRequests: 0,
|
||||
success: 0,
|
||||
rateLimited: 0,
|
||||
errors: 0,
|
||||
ipStats: {} // 每个IP的统计信息
|
||||
};
|
||||
|
||||
// 颜色输出
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
red: '\x1b[31m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
magenta: '\x1b[35m',
|
||||
cyan: '\x1b[36m'
|
||||
};
|
||||
|
||||
/**
|
||||
* 延迟函数
|
||||
* @param {number} ms 延迟毫秒数
|
||||
*/
|
||||
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* 生成随机IP地址
|
||||
*/
|
||||
function generateRandomIP() {
|
||||
const ip = [];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
ip.push(Math.floor(Math.random() * 256));
|
||||
}
|
||||
return ip.join('.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化IP统计
|
||||
*/
|
||||
function initIPStats() {
|
||||
const ips = [];
|
||||
for (let i = 0; i < SIMULATED_IPS; i++) {
|
||||
const ip = generateRandomIP();
|
||||
ips.push(ip);
|
||||
stats.ipStats[ip] = {
|
||||
success: 0,
|
||||
rateLimited: 0,
|
||||
errors: 0,
|
||||
totalRequests: 0
|
||||
};
|
||||
}
|
||||
return ips;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送单个请求
|
||||
* @param {string} ip 模拟的IP地址
|
||||
* @param {number} index 请求索引
|
||||
*/
|
||||
async function sendRequest(ip, index) {
|
||||
stats.totalRequests++;
|
||||
stats.ipStats[ip].totalRequests++;
|
||||
|
||||
const url = `${BASE_URL}${TEST_ENDPOINT}`;
|
||||
|
||||
try {
|
||||
const response = await axios({
|
||||
method: 'GET',
|
||||
url,
|
||||
headers: {
|
||||
'X-Forwarded-For': ip, // 模拟不同的IP地址
|
||||
'User-Agent': `TestBot/${ip}` // 模拟不同的用户代理
|
||||
},
|
||||
validateStatus: () => true // 不抛出HTTP错误
|
||||
});
|
||||
|
||||
// 处理响应
|
||||
if (response.status === 429) { // 请求被限速
|
||||
stats.rateLimited++;
|
||||
stats.ipStats[ip].rateLimited++;
|
||||
process.stdout.write(`${colors.yellow}L${colors.reset}`);
|
||||
} else if (response.status >= 200 && response.status < 300) { // 成功
|
||||
stats.success++;
|
||||
stats.ipStats[ip].success++;
|
||||
process.stdout.write(`${colors.green}.${colors.reset}`);
|
||||
} else { // 其他错误
|
||||
stats.errors++;
|
||||
stats.ipStats[ip].errors++;
|
||||
process.stdout.write(`${colors.red}E${colors.reset}`);
|
||||
}
|
||||
|
||||
return response.status;
|
||||
} catch (error) {
|
||||
stats.errors++;
|
||||
stats.ipStats[ip].errors++;
|
||||
process.stdout.write(`${colors.red}X${colors.reset}`);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印统计信息
|
||||
* @param {Array<string>} ips IP地址列表
|
||||
*/
|
||||
function printStats(ips) {
|
||||
console.log(`\n\n${colors.magenta}分布式测试结果:${colors.reset}`);
|
||||
console.log(`总请求数: ${stats.totalRequests}`);
|
||||
console.log(`成功请求: ${stats.success}`);
|
||||
console.log(`被限速请求: ${stats.rateLimited}`);
|
||||
console.log(`错误请求: ${stats.errors}`);
|
||||
console.log(`总限速比例: ${Math.round((stats.rateLimited / stats.totalRequests) * 100)}%`);
|
||||
|
||||
console.log(`\n${colors.magenta}各IP测试结果:${colors.reset}`);
|
||||
ips.forEach((ip, index) => {
|
||||
const ipStat = stats.ipStats[ip];
|
||||
const limitedPercent = Math.round((ipStat.rateLimited / ipStat.totalRequests) * 100);
|
||||
console.log(`IP-${index+1} (${ip}): 总请求=${ipStat.totalRequests}, 成功=${ipStat.success}, 限速=${ipStat.rateLimited} (${limitedPercent}%), 错误=${ipStat.errors}`);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 为单个IP发送多个请求
|
||||
* @param {string} ip IP地址
|
||||
*/
|
||||
async function sendRequestsForIP(ip) {
|
||||
const promises = [];
|
||||
for (let i = 0; i < REQUESTS_PER_IP; i++) {
|
||||
promises.push(sendRequest(ip, i));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
async function main() {
|
||||
console.log(`${colors.cyan}开始分布式测试限速功能...${colors.reset}`);
|
||||
console.log(`目标端点: ${TEST_ENDPOINT}`);
|
||||
console.log(`模拟IP数量: ${SIMULATED_IPS}`);
|
||||
console.log(`每个IP请求数: ${REQUESTS_PER_IP}`);
|
||||
console.log(`总请求数: ${SIMULATED_IPS * REQUESTS_PER_IP}`);
|
||||
|
||||
try {
|
||||
// 初始化IP统计
|
||||
const ips = initIPStats();
|
||||
|
||||
console.log(`\n${colors.cyan}测试进度:${colors.reset}`);
|
||||
|
||||
// 为每个IP发送请求
|
||||
for (let i = 0; i < ips.length; i++) {
|
||||
const ip = ips[i];
|
||||
console.log(`\n${colors.blue}测试IP-${i+1} (${ip}):${colors.reset} `);
|
||||
await sendRequestsForIP(ip);
|
||||
|
||||
// 在IP批次之间添加延迟
|
||||
if (i < ips.length - 1) {
|
||||
await delay(DELAY_BETWEEN_BATCHES);
|
||||
}
|
||||
}
|
||||
|
||||
printStats(ips);
|
||||
|
||||
console.log(`\n${colors.green}分布式测试完成!${colors.reset}`);
|
||||
} catch (error) {
|
||||
console.error(`\n${colors.red}测试过程中发生错误: ${error.message}${colors.reset}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行主函数
|
||||
main().catch(console.error);
|
@ -1,83 +0,0 @@
|
||||
import { spawn } from 'child_process';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
|
||||
// 获取当前文件的目录路径
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
// 颜色输出
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
red: '\x1b[31m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
magenta: '\x1b[35m',
|
||||
cyan: '\x1b[36m'
|
||||
};
|
||||
|
||||
/**
|
||||
* 运行脚本
|
||||
* @param {string} scriptName 脚本文件名
|
||||
* @param {string} description 测试描述
|
||||
*/
|
||||
function runScript(scriptName, description) {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(`\n${colors.cyan}=======================================${colors.reset}`);
|
||||
console.log(`${colors.cyan}运行: ${description}${colors.reset}`);
|
||||
console.log(`${colors.cyan}=======================================${colors.reset}\n`);
|
||||
|
||||
const scriptPath = join(__dirname, scriptName);
|
||||
const child = spawn('node', [scriptPath], { stdio: 'inherit' });
|
||||
|
||||
child.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
console.log(`\n${colors.green}${description}完成,退出码: ${code}${colors.reset}`);
|
||||
resolve();
|
||||
} else {
|
||||
console.error(`\n${colors.red}${description}失败,退出码: ${code}${colors.reset}`);
|
||||
reject(new Error(`脚本退出码: ${code}`));
|
||||
}
|
||||
});
|
||||
|
||||
child.on('error', (error) => {
|
||||
console.error(`\n${colors.red}启动脚本时出错: ${error.message}${colors.reset}`);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
async function main() {
|
||||
console.log(`${colors.magenta}开始运行所有限速测试...${colors.reset}`);
|
||||
|
||||
try {
|
||||
// 运行功能测试
|
||||
await runScript('test-rate-limit.js', '功能测试');
|
||||
|
||||
// 等待一段时间以确保限速计数器重置
|
||||
console.log(`\n${colors.yellow}等待30秒以确保限速计数器重置...${colors.reset}`);
|
||||
await new Promise(resolve => setTimeout(resolve, 30000));
|
||||
|
||||
// 运行压力测试
|
||||
await runScript('stress-test.js', '压力测试');
|
||||
|
||||
// 等待一段时间以确保限速计数器重置
|
||||
console.log(`\n${colors.yellow}等待30秒以确保限速计数器重置...${colors.reset}`);
|
||||
await new Promise(resolve => setTimeout(resolve, 30000));
|
||||
|
||||
// 运行分布式测试
|
||||
await runScript('distributed-test.js', '分布式测试');
|
||||
|
||||
console.log(`\n${colors.green}所有测试已完成!${colors.reset}`);
|
||||
} catch (error) {
|
||||
console.error(`\n${colors.red}测试过程中发生错误: ${error.message}${colors.reset}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行主函数
|
||||
main().catch(console.error);
|
@ -1,135 +0,0 @@
|
||||
import axios from 'axios';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
// 配置
|
||||
const BASE_URL = 'http://localhost:3000'; // 修改为你的服务器地址和端口
|
||||
const TEST_NAMESPACE = uuidv4(); // 生成随机命名空间用于测试
|
||||
const CONCURRENT_REQUESTS = 50; // 并发请求数
|
||||
const TOTAL_REQUESTS = 500; // 总请求数
|
||||
const TEST_ENDPOINT = '/check'; // 测试端点
|
||||
|
||||
// 测试结果统计
|
||||
const stats = {
|
||||
success: 0,
|
||||
rateLimited: 0,
|
||||
errors: 0,
|
||||
totalRequests: 0,
|
||||
startTime: 0,
|
||||
endTime: 0
|
||||
};
|
||||
|
||||
// 颜色输出
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
red: '\x1b[31m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
magenta: '\x1b[35m',
|
||||
cyan: '\x1b[36m'
|
||||
};
|
||||
|
||||
/**
|
||||
* 发送单个请求
|
||||
* @param {number} index 请求索引
|
||||
*/
|
||||
async function sendRequest(index) {
|
||||
stats.totalRequests++;
|
||||
const url = `${BASE_URL}${TEST_ENDPOINT}`;
|
||||
|
||||
try {
|
||||
const response = await axios({
|
||||
method: 'GET',
|
||||
url,
|
||||
validateStatus: () => true // 不抛出HTTP错误
|
||||
});
|
||||
|
||||
// 处理响应
|
||||
if (response.status === 429) { // 请求被限速
|
||||
stats.rateLimited++;
|
||||
process.stdout.write(`${colors.yellow}L${colors.reset}`);
|
||||
} else if (response.status >= 200 && response.status < 300) { // 成功
|
||||
stats.success++;
|
||||
process.stdout.write(`${colors.green}.${colors.reset}`);
|
||||
} else { // 其他错误
|
||||
stats.errors++;
|
||||
process.stdout.write(`${colors.red}E${colors.reset}`);
|
||||
}
|
||||
|
||||
// 每50个请求换行
|
||||
if (index % 50 === 0 && index > 0) {
|
||||
process.stdout.write('\n');
|
||||
}
|
||||
|
||||
return response.status;
|
||||
} catch (error) {
|
||||
stats.errors++;
|
||||
process.stdout.write(`${colors.red}X${colors.reset}`);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印统计信息
|
||||
*/
|
||||
function printStats() {
|
||||
const duration = (stats.endTime - stats.startTime) / 1000;
|
||||
const rps = Math.round(stats.totalRequests / duration);
|
||||
|
||||
console.log(`\n\n${colors.magenta}压力测试结果:${colors.reset}`);
|
||||
console.log(`总请求数: ${stats.totalRequests}`);
|
||||
console.log(`成功请求: ${stats.success}`);
|
||||
console.log(`被限速请求: ${stats.rateLimited}`);
|
||||
console.log(`错误请求: ${stats.errors}`);
|
||||
console.log(`限速比例: ${Math.round((stats.rateLimited / stats.totalRequests) * 100)}%`);
|
||||
console.log(`测试持续时间: ${duration.toFixed(2)}秒`);
|
||||
console.log(`平均请求速率: ${rps} 请求/秒`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量发送请求
|
||||
* @param {number} batchSize 批次大小
|
||||
* @param {number} startIndex 起始索引
|
||||
*/
|
||||
async function sendBatch(batchSize, startIndex) {
|
||||
const promises = [];
|
||||
for (let i = 0; i < batchSize; i++) {
|
||||
const index = startIndex + i;
|
||||
if (index < TOTAL_REQUESTS) {
|
||||
promises.push(sendRequest(index));
|
||||
}
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
async function main() {
|
||||
console.log(`${colors.cyan}开始压力测试限速功能...${colors.reset}`);
|
||||
console.log(`目标端点: ${TEST_ENDPOINT}`);
|
||||
console.log(`并发请求数: ${CONCURRENT_REQUESTS}`);
|
||||
console.log(`总请求数: ${TOTAL_REQUESTS}`);
|
||||
console.log(`\n${colors.cyan}测试进度:${colors.reset}`);
|
||||
|
||||
try {
|
||||
stats.startTime = Date.now();
|
||||
|
||||
// 分批发送请求
|
||||
const batches = Math.ceil(TOTAL_REQUESTS / CONCURRENT_REQUESTS);
|
||||
for (let i = 0; i < batches; i++) {
|
||||
const startIndex = i * CONCURRENT_REQUESTS;
|
||||
await sendBatch(CONCURRENT_REQUESTS, startIndex);
|
||||
}
|
||||
|
||||
stats.endTime = Date.now();
|
||||
printStats();
|
||||
|
||||
console.log(`\n${colors.green}压力测试完成!${colors.reset}`);
|
||||
} catch (error) {
|
||||
console.error(`\n${colors.red}测试过程中发生错误: ${error.message}${colors.reset}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行主函数
|
||||
main().catch(console.error);
|
@ -1,250 +0,0 @@
|
||||
import axios from 'axios';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
// 配置
|
||||
const BASE_URL = 'http://localhost:3030'; // 修改为你的服务器地址和端口
|
||||
const TEST_NAMESPACE = uuidv4(); // 生成随机命名空间用于测试
|
||||
const DELAY_BETWEEN_REQUESTS = 100; // 请求间隔时间(毫秒)
|
||||
|
||||
// 测试结果统计
|
||||
const stats = {
|
||||
success: 0,
|
||||
rateLimited: 0,
|
||||
errors: 0,
|
||||
totalRequests: 0
|
||||
};
|
||||
|
||||
// 颜色输出
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
red: '\x1b[31m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
magenta: '\x1b[35m',
|
||||
cyan: '\x1b[36m'
|
||||
};
|
||||
|
||||
/**
|
||||
* 延迟函数
|
||||
* @param {number} ms 延迟毫秒数
|
||||
*/
|
||||
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* 发送请求并处理响应
|
||||
* @param {string} method HTTP方法
|
||||
* @param {string} endpoint 请求路径
|
||||
* @param {object} data 请求数据
|
||||
* @param {number} index 请求索引
|
||||
*/
|
||||
async function sendRequest(method, endpoint, data = null, index) {
|
||||
stats.totalRequests++;
|
||||
const url = `${BASE_URL}${endpoint}`;
|
||||
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
const response = await axios({
|
||||
method,
|
||||
url,
|
||||
data,
|
||||
validateStatus: () => true // 不抛出HTTP错误
|
||||
});
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
// 处理响应
|
||||
if (response.status === 429) { // 请求被限速
|
||||
stats.rateLimited++;
|
||||
console.log(`${colors.yellow}[${index}] ${method} ${endpoint} - 被限速 (${duration}ms)${colors.reset}`);
|
||||
return { limited: true, status: response.status };
|
||||
} else if (response.status >= 200 && response.status < 300) { // 成功
|
||||
stats.success++;
|
||||
console.log(`${colors.green}[${index}] ${method} ${endpoint} - 成功: ${response.status} (${duration}ms)${colors.reset}`);
|
||||
return { limited: false, status: response.status, data: response.data };
|
||||
} else { // 其他错误
|
||||
stats.errors++;
|
||||
console.log(`${colors.red}[${index}] ${method} ${endpoint} - 错误: ${response.status} (${duration}ms)${colors.reset}`);
|
||||
return { limited: false, status: response.status, error: response.data };
|
||||
}
|
||||
} catch (error) {
|
||||
stats.errors++;
|
||||
console.log(`${colors.red}[${index}] ${method} ${endpoint} - 异常: ${error.message}${colors.reset}`);
|
||||
return { limited: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试全局限速
|
||||
*/
|
||||
async function testGlobalRateLimit() {
|
||||
console.log(`\n${colors.cyan}===== 测试全局限速 (200/15分钟) =====${colors.reset}`);
|
||||
const requests = [];
|
||||
|
||||
// 发送250个请求 (应该有50个被限速)
|
||||
for (let i = 0; i < 250; i++) {
|
||||
requests.push(sendRequest('GET', '/check', null, i));
|
||||
await delay(DELAY_BETWEEN_REQUESTS);
|
||||
}
|
||||
|
||||
await Promise.all(requests);
|
||||
printStats('全局限速测试');
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试API限速
|
||||
*/
|
||||
async function testApiRateLimit() {
|
||||
console.log(`\n${colors.cyan}===== 测试API限速 (50/5分钟) =====${colors.reset}`);
|
||||
resetStats();
|
||||
const requests = [];
|
||||
|
||||
// 发送60个请求 (应该有10个被限速)
|
||||
for (let i = 0; i < 60; i++) {
|
||||
requests.push(sendRequest('GET', '/check', null, i));
|
||||
await delay(DELAY_BETWEEN_REQUESTS);
|
||||
}
|
||||
|
||||
await Promise.all(requests);
|
||||
printStats('API限速测试');
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试写操作限速
|
||||
*/
|
||||
async function testWriteRateLimit() {
|
||||
console.log(`\n${colors.cyan}===== 测试写操作限速 (10/分钟) =====${colors.reset}`);
|
||||
resetStats();
|
||||
const requests = [];
|
||||
|
||||
// 发送15个写请求 (应该有5个被限速)
|
||||
for (let i = 0; i < 15; i++) {
|
||||
const key = `test-key-${i}`;
|
||||
const data = { value: `test-value-${i}`, timestamp: Date.now() };
|
||||
requests.push(sendRequest('POST', `/${TEST_NAMESPACE}/${key}`, data, i));
|
||||
await delay(DELAY_BETWEEN_REQUESTS);
|
||||
}
|
||||
|
||||
await Promise.all(requests);
|
||||
printStats('写操作限速测试');
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试删除操作限速
|
||||
*/
|
||||
async function testDeleteRateLimit() {
|
||||
console.log(`\n${colors.cyan}===== 测试删除操作限速 (5/5分钟) =====${colors.reset}`);
|
||||
resetStats();
|
||||
|
||||
// 先创建几个键值对
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const key = `delete-key-${i}`;
|
||||
const data = { value: `delete-value-${i}` };
|
||||
await sendRequest('POST', `/${TEST_NAMESPACE}/${key}`, data, `创建-${i}`);
|
||||
await delay(DELAY_BETWEEN_REQUESTS);
|
||||
}
|
||||
|
||||
resetStats();
|
||||
const requests = [];
|
||||
|
||||
// 发送8个删除请求 (应该有3个被限速)
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const key = `delete-key-${i}`;
|
||||
requests.push(sendRequest('DELETE', `/${TEST_NAMESPACE}/${key}`, null, i));
|
||||
await delay(DELAY_BETWEEN_REQUESTS);
|
||||
}
|
||||
|
||||
await Promise.all(requests);
|
||||
printStats('删除操作限速测试');
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试认证限速
|
||||
*/
|
||||
async function testAuthRateLimit() {
|
||||
console.log(`\n${colors.cyan}===== 测试认证限速 (5/30分钟) =====${colors.reset}`);
|
||||
resetStats();
|
||||
const requests = [];
|
||||
|
||||
// 发送8个认证请求 (应该有3个被限速)
|
||||
for (let i = 0; i < 8; i++) {
|
||||
requests.push(sendRequest('POST', '/auth', { username: 'test', password: 'wrong' }, i));
|
||||
await delay(DELAY_BETWEEN_REQUESTS);
|
||||
}
|
||||
|
||||
await Promise.all(requests);
|
||||
printStats('认证限速测试');
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置统计数据
|
||||
*/
|
||||
function resetStats() {
|
||||
stats.success = 0;
|
||||
stats.rateLimited = 0;
|
||||
stats.errors = 0;
|
||||
stats.totalRequests = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印统计信息
|
||||
*/
|
||||
function printStats(testName) {
|
||||
console.log(`\n${colors.magenta}${testName}结果:${colors.reset}`);
|
||||
console.log(`总请求数: ${stats.totalRequests}`);
|
||||
console.log(`成功请求: ${stats.success}`);
|
||||
console.log(`被限速请求: ${stats.rateLimited}`);
|
||||
console.log(`错误请求: ${stats.errors}`);
|
||||
console.log(`限速比例: ${Math.round((stats.rateLimited / stats.totalRequests) * 100)}%`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
async function main() {
|
||||
console.log(`${colors.cyan}开始测试限速功能...${colors.reset}`);
|
||||
console.log(`使用测试命名空间: ${TEST_NAMESPACE}`);
|
||||
|
||||
try {
|
||||
// 测试全局限速
|
||||
await testGlobalRateLimit();
|
||||
|
||||
// 重置统计并等待一段时间
|
||||
resetStats();
|
||||
await delay(2000);
|
||||
|
||||
// 测试API限速
|
||||
await testApiRateLimit();
|
||||
|
||||
// 重置统计并等待一段时间
|
||||
resetStats();
|
||||
await delay(2000);
|
||||
|
||||
// 测试写操作限速
|
||||
await testWriteRateLimit();
|
||||
|
||||
// 重置统计并等待一段时间
|
||||
resetStats();
|
||||
await delay(2000);
|
||||
|
||||
// 测试删除操作限速
|
||||
await testDeleteRateLimit();
|
||||
|
||||
// 重置统计并等待一段时间
|
||||
resetStats();
|
||||
await delay(2000);
|
||||
|
||||
// 测试认证限速
|
||||
await testAuthRateLimit();
|
||||
|
||||
// 清理测试数据
|
||||
console.log(`\n${colors.cyan}清理测试数据...${colors.reset}`);
|
||||
await sendRequest('DELETE', `/${TEST_NAMESPACE}`, null, 'cleanup');
|
||||
|
||||
console.log(`\n${colors.green}所有测试完成!${colors.reset}`);
|
||||
} catch (error) {
|
||||
console.error(`${colors.red}测试过程中发生错误: ${error.message}${colors.reset}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行主函数
|
||||
main().catch(console.error);
|
@ -1,4 +1,4 @@
|
||||
import kvStore from "../models/kvStore.js";
|
||||
import kvStore from "./kvStore.js";
|
||||
|
||||
// 存储 readme 值的内存变量
|
||||
let readmeValue = null;
|
||||
|
Loading…
x
Reference in New Issue
Block a user