速率限制
理解 API 速率限制与并发,以及如何在这些限制范围内进行操作
概述
速率限制旨在防止 API 遭到滥用,并确保所有用户都能公平使用。Moknah 针对 HTTP 和 WebSocket 端点的请求频率、额度消耗及并发连接数实施了相应的限制措施。
Moknah 进行精确处理 每位用户同一时间仅限一项操作.
• HTTP: 请等待当前请求完成后再发送下一个请求。并发请求将收到 409 Conflict(冲突)或 429 Too Many Requests(请求过多)的响应。
• WebSocket: 您同一时间只能保持一个活跃的 WebSocket 连接。如果尝试建立第二个连接,该连接将立即关闭,并返回错误代码 4290。
默认限制
| 限制类型 | 价值 | 适用于 | 描述 |
|---|---|---|---|
| 每分钟请求数 (RPM) | 60 |
仅限 HTTP | 每分钟标准 API POST 请求的最大数量。 |
| 每分钟点数 (CPM) | 10,000 |
HTTP 与 WebSocket | 每分钟消耗的最大点数。 |
| 活动连接 | 1 |
仅限 WebSocket | 每个用户同时进行的 WSS 流的最大数量。 |
WebSocket 限制与关闭代码
由于 WebSocket 维持的是持久连接,因此不使用 HTTP 标头进行速率限制。取而代之的是,限制措施通过断开连接和 JSON 控制消息来实施:
| 已达上限 | 行为 / 输出 |
|---|---|
| 并发限制 | 连接立即关闭,代码为 4290。 |
| 额度已用尽 | 接收到 JSON 错误并关闭。 |
| 数据块大小限制 | 如果单个文本片段超过 2,000 个字符,则会收到 JSON 错误。 |
| 消息速率限制 | 如果发送文本片段的速度超过了队列的处理速度,将会收到 JSON 错误。 |
注意:如果 WebSocket 意外断开连接,请至少等待 15 秒后再重新连接,以确保服务器端的锁已完全清除。
HTTP 速率限制标头
对于标准的 REST 端点,每个 API 响应都包含有助于您追踪状态的标头:
| 页眉 | 描述 | 示例 |
|---|---|---|
RateLimit-Limit |
您的转速限制 | 60 |
RateLimit-Remaining |
本分钟剩余请求数 | 45 |
RateLimit-Reset |
限额重置时的 Unix 时间戳 | 1699574460 |
Moknah-Credits-Remaining |
本分钟内仍有可用额度 | 10000 |
Moknah-Credits-Used |
本分钟消耗的额度 | 5000 |
当受到 HTTP 速率限制时
如果您超过了 HTTP 速率限制,将会收到一个 429 Too Many Requests 响应,其中包含以下附加标头:
| 页眉 | 描述 | 示例 |
|---|---|---|
Retry-After |
重试前等待的秒数 | 45 |
RateLimit-Reset |
可重试的 Unix 时间戳 | 1699574460 |
学分计算
额度根据文本长度和处理选项进行计算。对于 WebSocket,额度会在数据流传输过程中按数据块动态扣除。
| 因素 | 乘数 | 描述 |
|---|---|---|
| 基础成本 | 1x |
每个字符 1 点积分 |
| AI 增强型归一化 | 2x |
带变音符号的阿拉伯语高级处理 |
| 优质语音 | +% |
优质语音的额外百分比 |
示例: 经AI增强规范化处理的500字文本:
500 characters × 2 (AI-Enhanced) = 1,000 credits
最佳实践
1. 针对海量顺序文本使用 WebSocket
若需生成对话式 AI 响应或解析长文档,请勿连续快速发送 50 个 HTTP POST 请求。建议建立单个 WebSocket 连接,并通过该持久连接以流式方式传输数据片段。这样可以完全绕过每分钟 60 次请求(RPM)的 HTTP 限制(但仍受额度限制约束)。
2. 实现请求排队 (HTTP)
由于 Moknah 不支持并发请求,请将 HTTP 请求放入队列并按顺序处理:
import queue
import threading
import time
class TTSQueue:
def __init__(self, api_key):
self.api_key = api_key
self.queue = queue.Queue()
self.worker = threading.Thread(target=self._process_queue, daemon=True)
self.worker.start()
def _process_queue(self):
while True:
text, voice_id, callback = self.queue.get()
try:
result = self._generate(text, voice_id)
callback(result, None)
except Exception as e:
callback(None, e)
finally:
self.queue.task_done()
def _generate(self, text, voice_id):
# Your API call here
pass
def add(self, text, voice_id, callback):
self.queue.put((text, voice_id, callback))
# Usage
tts = TTSQueue("your_api_key")
tts.add("Hello world", "voice_123", lambda r, e: print(r or e))
class TTSQueue {
constructor(apiKey) {
this.apiKey = apiKey;
this.queue = [];
this.processing = false;
}
async add(text, voiceId) {
return new Promise((resolve, reject) => {
this.queue.push({ text, voiceId, resolve, reject });
this.processNext();
});
}
async processNext() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
const { text, voiceId, resolve, reject } = this.queue.shift();
try {
const result = await this.generate(text, voiceId);
resolve(result);
} catch (error) {
reject(error);
} finally {
this.processing = false;
this.processNext(); // Process next in queue
}
}
async generate(text, voiceId) {
// Your API call here
}
}
// Usage
const tts = new TTSQueue("your_api_key");
const audio = await tts.add("Hello world", "voice_123");
2. 实现请求排队 (HTTP)
由于 Moknah 不支持并发请求,请将 HTTP 请求放入队列并按顺序处理:
import time
import random
def request_with_backoff(func, max_retries=5):
retries = 0
while retries < max_retries:
try:
response = func()
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
# Add jitter to prevent thundering herd
wait_time = retry_after + random.uniform(0, 1)
print(f"Rate limited. Waiting {wait_time:.1f}s...")
time.sleep(wait_time)
retries += 1
continue
return response
except Exception as e:
# Exponential backoff for other errors
wait_time = (2 ** retries) + random.uniform(0, 1)
print(f"Error: {e}. Retrying in {wait_time:.1f}s...")
time.sleep(wait_time)
retries += 1
raise Exception("Max retries exceeded")
async function requestWithBackoff(func, maxRetries = 5) {
let retries = 0;
while (retries < maxRetries) {
try {
const response = await func();
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get("Retry-After") || 60);
// Add jitter to prevent thundering herd
const waitTime = retryAfter + Math.random();
console.log(`Rate limited. Waiting ${waitTime.toFixed(1)}s...`);
await new Promise(r => setTimeout(r, waitTime * 1000));
retries++;
continue;
}
return response;
} catch (error) {
// Exponential backoff for other errors
const waitTime = Math.pow(2, retries) + Math.random();
console.log(`Error: ${error.message}. Retrying in ${waitTime.toFixed(1)}s...`);
await new Promise(r => setTimeout(r, waitTime * 1000));
retries++;
}
}
throw new Error("Max retries exceeded");
}
3. 实现指数退避
当遇到速率限制 (HTTP 429) 或连接限制 (WebSocket 4290) 时,请使用指数退避策略进行安全重试,以避免向服务器发送过多请求:
摘要
HTTP 限制: 每分钟 60 次请求,1 个并发请求。
WebSocket 限制: 1 个并发连接,每个数据块 2,000 个字符。
全局限制: 10,000 CPM(每分钟点数)。
需要更多容量吗? 电子邮件 sales@moknah.io
如有关于 API 的问题或疑问,请联系我们: api@moknah.io.