diff --git a/API_KEY_EDIT_FEATURE.md b/API_KEY_EDIT_FEATURE.md deleted file mode 100644 index 60e8b2d5..00000000 --- a/API_KEY_EDIT_FEATURE.md +++ /dev/null @@ -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 列表添加编辑按钮 -- ���建编辑模态框,仅显示可编辑字段 -- 使用 PUT 请求更新 API Key - -### 后端部分 -- 更新路由只接受 tokenLimit 和 concurrencyLimit 参数 -- 严格验证参数类型和范围 -- 使用现有的 apiKeyService.updateApiKey 方法 - -### 安全性 -- 需要管理员认证才能编辑 -- 只允许修改限制相关参数 -- 不会暴露敏感信息(如 API Key 值) - -## API 接口 - -``` -PUT /admin/api-keys/:keyId -Authorization: Bearer -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 支持。 \ No newline at end of file diff --git a/CONCURRENCY_CONTROL.md b/CONCURRENCY_CONTROL.md deleted file mode 100644 index 63a2a0c4..00000000 --- a/CONCURRENCY_CONTROL.md +++ /dev/null @@ -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 中删除对应的键。 \ No newline at end of file diff --git a/test-concurrency.js b/test-concurrency.js deleted file mode 100644 index 20e54e99..00000000 --- a/test-concurrency.js +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/env node - -// 并发控制功能测试脚本 -// 用法: node test-concurrency.js <并发数> - -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 <并发数>'); - 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(`�� 服务器: ${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); \ No newline at end of file