mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 21:17:30 +00:00
chore: 移除测试脚本和文档文件
- 删除 test-concurrency.js 测试脚本 - 删除 CONCURRENCY_CONTROL.md 文档 - 删除 API_KEY_EDIT_FEATURE.md 文档 保留核心功能代码,移除仅用于开发测试的文件 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,88 +0,0 @@
|
|||||||
# API Key 编辑功能
|
|
||||||
|
|
||||||
## 功能说明
|
|
||||||
|
|
||||||
现在支持在管理后台编辑已创建的 API Key,可以修改以下参数:
|
|
||||||
|
|
||||||
- **Token 限制**:设置该 API Key 的最大 token 使用量
|
|
||||||
- **并发限制**:设置该 API Key 可同时处理的最大请求数
|
|
||||||
|
|
||||||
## 使用方法
|
|
||||||
|
|
||||||
1. **进入管理后台**
|
|
||||||
- 登录管理后台
|
|
||||||
- 进入 "API Keys" 标签页
|
|
||||||
|
|
||||||
2. **编辑 API Key**
|
|
||||||
- 在 API Key 列表中找到要编辑的项
|
|
||||||
- 点击 "编辑" 按钮(蓝色铅笔图标)
|
|
||||||
- 在弹出的编辑框中修改参数:
|
|
||||||
- Token 限制:输入数字,0 或留空表示无限制
|
|
||||||
- 并发限制:输入数字,0 或留空表示无限制
|
|
||||||
- 点击 "保存修改" 完成编辑
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **不可修改的字段**
|
|
||||||
- API Key 名称不可修改(显示为灰色禁用状态)
|
|
||||||
- API Key 的密钥值不可修改
|
|
||||||
- 描述信息不可修改
|
|
||||||
|
|
||||||
2. **参数验证**
|
|
||||||
- Token 限制和并发限制必须为非负整数
|
|
||||||
- 0 表示无限制
|
|
||||||
- 留空也表示无限制
|
|
||||||
|
|
||||||
3. **即时生效**
|
|
||||||
- 修改保存后立即生效
|
|
||||||
- 正在进行的请求不受影响
|
|
||||||
- 新的请求将使用更新后的限制
|
|
||||||
|
|
||||||
## 技术实现
|
|
||||||
|
|
||||||
### 前端部分
|
|
||||||
- 在 API Key 列表添加编辑按钮
|
|
||||||
- <20><><EFBFBD>建编辑模态框,仅显示可编辑字段
|
|
||||||
- 使用 PUT 请求更新 API Key
|
|
||||||
|
|
||||||
### 后端部分
|
|
||||||
- 更新路由只接受 tokenLimit 和 concurrencyLimit 参数
|
|
||||||
- 严格验证参数类型和范围
|
|
||||||
- 使用现有的 apiKeyService.updateApiKey 方法
|
|
||||||
|
|
||||||
### 安全性
|
|
||||||
- 需要管理员认证才能编辑
|
|
||||||
- 只允许修改限制相关参数
|
|
||||||
- 不会暴露敏感信息(如 API Key 值)
|
|
||||||
|
|
||||||
## API 接口
|
|
||||||
|
|
||||||
```
|
|
||||||
PUT /admin/api-keys/:keyId
|
|
||||||
Authorization: Bearer <admin_token>
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"tokenLimit": 1000000,
|
|
||||||
"concurrencyLimit": 5
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
响应:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"message": "API key updated successfully"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 常见问题
|
|
||||||
|
|
||||||
**Q: 为什么不能修改 API Key 的名称?**
|
|
||||||
A: 为了保持数据一致性和避免混淆,API Key 创建后名称不可修改。
|
|
||||||
|
|
||||||
**Q: 修改限制后,已经超过限制的请求会怎样?**
|
|
||||||
A: 已经在处理中的请求不受影响,新的请求将受到新限制的约束。
|
|
||||||
|
|
||||||
**Q: 可以通过 CLI 或 API 修改吗?**
|
|
||||||
A: 目前仅支持通过管理后台修改,后续可能会添加 CLI 支持。
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
# API Key 并发控制功能
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
Claude Relay Service 现在支持为每个 API Key 设置并发请求限制。这个功能可以帮助:
|
|
||||||
|
|
||||||
- 防止单个 API Key 占用过多资源
|
|
||||||
- 控制对 Claude API 的并发访问
|
|
||||||
- 为不同用户/应用分配不同的并发配额
|
|
||||||
- 保护服务稳定性
|
|
||||||
|
|
||||||
## 使用方法
|
|
||||||
|
|
||||||
### 1. 创建带并发限制的 API Key
|
|
||||||
|
|
||||||
在管理后台创建 API Key 时,可以设置"并发限制"字段:
|
|
||||||
|
|
||||||
- **0 或留空**:无并发限制(默认)
|
|
||||||
- **正整数**:限制同时处理的最大请求数
|
|
||||||
|
|
||||||
例如:设置为 5,则该 API Key 最多同时处理 5 个请求。
|
|
||||||
|
|
||||||
### 2. 并发控制行为
|
|
||||||
|
|
||||||
当请求超过并发限制时:
|
|
||||||
|
|
||||||
- HTTP 状态码:`429 Too Many Requests`
|
|
||||||
- 响应内容:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"error": "Concurrency limit exceeded",
|
|
||||||
"message": "Too many concurrent requests. Limit: 5 concurrent requests",
|
|
||||||
"currentConcurrency": 5,
|
|
||||||
"concurrencyLimit": 5
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 查看并发限制
|
|
||||||
|
|
||||||
在管理后台的 API Keys 列表中,每个 Key 都会显示其并发限制设置:
|
|
||||||
|
|
||||||
- 显示为具体数字(如 "5")表示有限制
|
|
||||||
- 显示为 "无限制" 表示没有并发限制
|
|
||||||
|
|
||||||
## 技术实现
|
|
||||||
|
|
||||||
### Redis 键结构
|
|
||||||
|
|
||||||
并发计数器存储在 Redis 中:
|
|
||||||
- 键名:`concurrency:{apiKeyId}`
|
|
||||||
- 过期时间:5分钟(防止异常情况下计数器不归零)
|
|
||||||
|
|
||||||
### 并发控制流程
|
|
||||||
|
|
||||||
1. 请求到达时,增加并发计数
|
|
||||||
2. 如果超过限制,立即拒绝并减少计数
|
|
||||||
3. 请求处理完成后,自动减少计数
|
|
||||||
4. 支持正常完成和异常中断的清理
|
|
||||||
|
|
||||||
## 测试并发控制
|
|
||||||
|
|
||||||
使用提供的测试脚本:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 测试 10 个并发请求
|
|
||||||
node test-concurrency.js cr_your_api_key_here 10
|
|
||||||
|
|
||||||
# 使用自定义服务器地址
|
|
||||||
SERVER_URL=http://your-server:3000 node test-concurrency.js cr_your_api_key_here 20
|
|
||||||
```
|
|
||||||
|
|
||||||
测试脚本会:
|
|
||||||
- 同时发送指定数量的请求
|
|
||||||
- 显示每个请求的结果
|
|
||||||
- 统计成功、被限流和错误的请求数
|
|
||||||
- 验证并发控制是否正常工作
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **兼容性**:新功能完全向后兼容,现有 API Key 默认无并发限制
|
|
||||||
2. **性能**:并发控制使用 Redis 原子操作,性能影响极小
|
|
||||||
3. **清理机制**:请求结束时自动清理计数,异常情况有过期时间保护
|
|
||||||
4. **监控**:所有并发限制触发都会记录在日志中
|
|
||||||
|
|
||||||
## 常见问题
|
|
||||||
|
|
||||||
**Q: 并发限制会影响流式响应吗?**
|
|
||||||
A: 不会。并发限制只在请求开始时检查,一旦请求被接受,流式响应会正常进行。
|
|
||||||
|
|
||||||
**Q: 如何修改现有 API Key 的并发限制?**
|
|
||||||
A: 目前需要在管理后台编辑 API Key,后续会支持此功能。
|
|
||||||
|
|
||||||
**Q: 并发计数不准确怎么办?**
|
|
||||||
A: 并发计数器有 5 分钟过期时间,会自动重置。如需立即重置,可以在 Redis 中删除对应的键。
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
// 并发控制功能测试脚本
|
|
||||||
// 用法: node test-concurrency.js <API_KEY> <并发数>
|
|
||||||
|
|
||||||
const https = require('https');
|
|
||||||
const http = require('http');
|
|
||||||
|
|
||||||
const API_KEY = process.argv[2];
|
|
||||||
const CONCURRENCY = parseInt(process.argv[3]) || 10;
|
|
||||||
const SERVER_URL = process.env.SERVER_URL || 'http://localhost:3000';
|
|
||||||
|
|
||||||
if (!API_KEY) {
|
|
||||||
console.error('请提供API Key: node test-concurrency.js <API_KEY> <并发数>');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析URL
|
|
||||||
const url = new URL(SERVER_URL);
|
|
||||||
const protocol = url.protocol === 'https:' ? https : http;
|
|
||||||
|
|
||||||
// 发送单个请求
|
|
||||||
function sendRequest(index) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const startTime = Date.now();
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
hostname: url.hostname,
|
|
||||||
port: url.port,
|
|
||||||
path: '/api/v1/messages',
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'x-api-key': API_KEY
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const req = protocol.request(options, (res) => {
|
|
||||||
let data = '';
|
|
||||||
|
|
||||||
res.on('data', (chunk) => {
|
|
||||||
data += chunk;
|
|
||||||
});
|
|
||||||
|
|
||||||
res.on('end', () => {
|
|
||||||
const duration = Date.now() - startTime;
|
|
||||||
|
|
||||||
if (res.statusCode === 429) {
|
|
||||||
try {
|
|
||||||
const response = JSON.parse(data);
|
|
||||||
resolve({
|
|
||||||
index,
|
|
||||||
status: res.statusCode,
|
|
||||||
error: response.error,
|
|
||||||
message: response.message,
|
|
||||||
concurrencyLimit: response.concurrencyLimit,
|
|
||||||
currentConcurrency: response.currentConcurrency,
|
|
||||||
duration
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
resolve({
|
|
||||||
index,
|
|
||||||
status: res.statusCode,
|
|
||||||
error: 'Rate limit exceeded',
|
|
||||||
message: data,
|
|
||||||
duration
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
||||||
resolve({
|
|
||||||
index,
|
|
||||||
status: res.statusCode,
|
|
||||||
success: true,
|
|
||||||
duration
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
resolve({
|
|
||||||
index,
|
|
||||||
status: res.statusCode,
|
|
||||||
error: 'Request failed',
|
|
||||||
message: data,
|
|
||||||
duration
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
req.on('error', (error) => {
|
|
||||||
reject({
|
|
||||||
index,
|
|
||||||
error: error.message,
|
|
||||||
duration: Date.now() - startTime
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 发送测试请求
|
|
||||||
const testData = JSON.stringify({
|
|
||||||
model: 'claude-3-haiku-20240307',
|
|
||||||
messages: [
|
|
||||||
{
|
|
||||||
role: 'user',
|
|
||||||
content: `测试并发请求 #${index}`
|
|
||||||
}
|
|
||||||
],
|
|
||||||
max_tokens: 10,
|
|
||||||
stream: false
|
|
||||||
});
|
|
||||||
|
|
||||||
req.write(testData);
|
|
||||||
req.end();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运行并发测试
|
|
||||||
async function runConcurrencyTest() {
|
|
||||||
console.log(`\n🧪 开始并发控制测试`);
|
|
||||||
console.log(`<EFBFBD><EFBFBD> 服务器: ${SERVER_URL}`);
|
|
||||||
console.log(`🔑 API Key: ${API_KEY.substring(0, 10)}...`);
|
|
||||||
console.log(`🔄 并发请求数: ${CONCURRENCY}`);
|
|
||||||
console.log(`⏰ 开始时间: ${new Date().toISOString()}\n`);
|
|
||||||
|
|
||||||
// 创建并发请求
|
|
||||||
const promises = [];
|
|
||||||
for (let i = 1; i <= CONCURRENCY; i++) {
|
|
||||||
promises.push(sendRequest(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 等待所有请求完成
|
|
||||||
try {
|
|
||||||
const results = await Promise.all(promises);
|
|
||||||
|
|
||||||
// 统计结果
|
|
||||||
let successCount = 0;
|
|
||||||
let rateLimitCount = 0;
|
|
||||||
let errorCount = 0;
|
|
||||||
let concurrencyLimit = null;
|
|
||||||
let maxConcurrency = 0;
|
|
||||||
|
|
||||||
console.log('📊 请求结果:\n');
|
|
||||||
|
|
||||||
results.forEach(result => {
|
|
||||||
if (result.success) {
|
|
||||||
successCount++;
|
|
||||||
console.log(`✅ 请求 #${result.index}: 成功 (${result.duration}ms)`);
|
|
||||||
} else if (result.status === 429) {
|
|
||||||
rateLimitCount++;
|
|
||||||
if (result.concurrencyLimit) {
|
|
||||||
concurrencyLimit = result.concurrencyLimit;
|
|
||||||
if (result.currentConcurrency > maxConcurrency) {
|
|
||||||
maxConcurrency = result.currentConcurrency;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log(`🚫 请求 #${result.index}: ${result.message} (${result.duration}ms)`);
|
|
||||||
} else {
|
|
||||||
errorCount++;
|
|
||||||
console.log(`❌ 请求 #${result.index}: ${result.error} - ${result.message} (${result.duration}ms)`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 打印统计信息
|
|
||||||
console.log('\n📈 测试统计:');
|
|
||||||
console.log(`✅ 成功请求: ${successCount}`);
|
|
||||||
console.log(`🚫 被限流请求: ${rateLimitCount}`);
|
|
||||||
console.log(`❌ 错误请求: ${errorCount}`);
|
|
||||||
|
|
||||||
if (concurrencyLimit !== null) {
|
|
||||||
console.log(`\n🔐 并发限制信息:`);
|
|
||||||
console.log(`📏 配置的并发限制: ${concurrencyLimit}`);
|
|
||||||
console.log(`📊 检测到的最大并发数: ${maxConcurrency}`);
|
|
||||||
|
|
||||||
if (successCount === concurrencyLimit && rateLimitCount === CONCURRENCY - concurrencyLimit) {
|
|
||||||
console.log(`\n✅ 并发控制工作正常!成功限制了并发数为 ${concurrencyLimit}`);
|
|
||||||
}
|
|
||||||
} else if (successCount === CONCURRENCY) {
|
|
||||||
console.log(`\n✅ 所有请求都成功了,该 API Key 没有并发限制或限制大于 ${CONCURRENCY}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('\n❌ 测试失败:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运行测试
|
|
||||||
runConcurrencyTest().catch(console.error);
|
|
||||||
Reference in New Issue
Block a user