Rate Limiting
ΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡ ΡΠ°ΡΡΠΎΡΡ Π·Π°ΠΏΡΠΎΡΠΎΠ² Π΄Π»Ρ Π·Π°ΡΠΈΡΡ ΠΎΡ Π·Π»ΠΎΡΠΏΠΎΡΡΠ΅Π±Π»Π΅Π½ΠΈΠΉ
ΠΠ±Π·ΠΎΡ
Enot Messenger ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ Redis-based rate limiting Π΄Π»Ρ Π·Π°ΡΠΈΡΡ ΠΎΡ Π·Π»ΠΎΡΠΏΠΎΡΡΠ΅Π±Π»Π΅Π½ΠΈΠΉ ΠΈ DDoS Π°ΡΠ°ΠΊ. ΠΠΈΠΌΠΈΡΡ ΠΏΡΠΈΠΌΠ΅Π½ΡΡΡΡΡ Π½Π° ΡΡΠΎΠ²Π½Π΅ IP Π°Π΄ΡΠ΅ΡΠ° ΠΈ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ.
ΠΠΈΠΌΠΈΡΡ Π·Π°ΠΏΡΠΎΡΠΎΠ²
| ΠΠΏΠ΅ΡΠ°ΡΠΈΡ | ΠΠΈΠΌΠΈΡ | ΠΠ΅ΡΠΈΠΎΠ΄ | Scope |
|---|---|---|---|
| Login | 5 ΠΏΠΎΠΏΡΡΠΎΠΊ | 15 ΠΌΠΈΠ½ΡΡ | IP Π°Π΄ΡΠ΅Ρ |
| Registration | 3 Π°ΠΊΠΊΠ°ΡΠ½ΡΠ° | 1 ΡΠ°Ρ | IP Π°Π΄ΡΠ΅Ρ |
| API Requests | 100 Π·Π°ΠΏΡΠΎΡΠΎΠ² | 1 ΠΌΠΈΠ½ΡΡΠ° | User ID |
| Messages | 30 ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ | 1 ΠΌΠΈΠ½ΡΡΠ° | User ID |
| File Upload | 10 ΡΠ°ΠΉΠ»ΠΎΠ² | 1 ΠΌΠΈΠ½ΡΡΠ° | User ID |
| WebSocket Messages | 60-120 ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ | 1 ΠΌΠΈΠ½ΡΡΠ° | User ID |
Response Headers
ΠΡΠΈ Π΄ΠΎΡΡΠΈΠΆΠ΅Π½ΠΈΠΈ Π»ΠΈΠΌΠΈΡΠ° API Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ ΡΠ»Π΅Π΄ΡΡΡΠΈΠ΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1714932000
Retry-After: 60
ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΎΠ²
-
X-RateLimit-Limit- ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠ΅ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π·Π°ΠΏΡΠΎΡΠΎΠ² -
X-RateLimit-Remaining- ΠΎΡΡΠ°Π²ΡΠ΅Π΅ΡΡ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π·Π°ΠΏΡΠΎΡΠΎΠ² -
X-RateLimit-Reset- Unix timestamp ΠΊΠΎΠ³Π΄Π° Π»ΠΈΠΌΠΈΡ ΡΠ±ΡΠΎΡΠΈΡΡΡ -
Retry-After- ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΡΠ΅ΠΊΡΠ½Π΄ Π΄ΠΎ ΡΠ±ΡΠΎΡΠ° Π»ΠΈΠΌΠΈΡΠ°
ΠΡΠ²Π΅Ρ ΠΏΡΠΈ ΠΏΡΠ΅Π²ΡΡΠ΅Π½ΠΈΠΈ Π»ΠΈΠΌΠΈΡΠ°
HTTP Status: 429 Too Many Requests
{
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please try again later.",
"details": {
"retry_after": 60,
"limit": 100,
"window": "1 minute"
}
}
ΠΠ°ΠΆΠ½ΠΎ: ΠΡΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠΈ 429 ΠΎΡΠΈΠ±ΠΊΠΈ, ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈΡΠ΅ ΡΠΊΠ°Π·Π°Π½Π½ΠΎΠ΅ Π² retry_after ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΡΠ΅ΠΊΡΠ½Π΄ ΠΏΠ΅ΡΠ΅Π΄ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΌ Π·Π°ΠΏΡΠΎΡΠΎΠΌ.
Best Practices
1. Exponential Backoff
ΠΡΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠΈ 429 ΠΎΡΠΈΠ±ΠΊΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ exponential backoff:
async function fetchWithRetry(url, options, maxRetries = 3) {
let retries = 0;
let delay = 1000; // 1 second
while (retries < maxRetries) {
try {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : delay;
console.log(`Rate limited. Waiting ${waitTime}ms...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
retries++;
delay *= 2; // Exponential backoff
continue;
}
return response;
} catch (error) {
throw error;
}
}
throw new Error('Max retries exceeded');
}
2. Request Batching
ΠΡΡΠΏΠΏΠΈΡΡΠΉΡΠ΅ Π·Π°ΠΏΡΠΎΡΡ Π²ΠΌΠ΅ΡΡΠΎ ΠΎΡΠΏΡΠ°Π²ΠΊΠΈ ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²Π° ΠΌΠ΅Π»ΠΊΠΈΡ :
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΏΠ°Π³ΠΈΠ½Π°ΡΠΈΡ Π΄Π»Ρ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ Π±ΠΎΠ»ΡΡΠΈΡ ΡΠΏΠΈΡΠΊΠΎΠ²
- ΠΡΡΠΈΡΡΠΉΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎ
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ WebSocket Π΄Π»Ρ real-time ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ Π²ΠΌΠ΅ΡΡΠΎ polling
3. ΠΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³ Π»ΠΈΠΌΠΈΡΠΎΠ²
ΠΡΡΠ»Π΅ΠΆΠΈΠ²Π°ΠΉΡΠ΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ rate limit ΠΈ ΠΏΡΠ΅Π΄ΡΠΏΡΠ΅ΠΆΠ΄Π°ΠΉΡΠ΅ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ:
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
const limit = parseInt(response.headers.get('X-RateLimit-Limit'));
if (remaining < limit * 0.1) {
console.warn('Approaching rate limit:', remaining, '/', limit);
}
ΠΠ°ΠΊ ΠΈΠ·Π±Π΅ΠΆΠ°ΡΡ rate limits
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ WebSocket Π΄Π»Ρ real-time ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ Π²ΠΌΠ΅ΡΡΠΎ polling
- ΠΡΡΠΈΡΡΠΉΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎ (localStorage, IndexedDB)
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΏΠ°Π³ΠΈΠ½Π°ΡΠΈΡ Π΄Π»Ρ Π±ΠΎΠ»ΡΡΠΈΡ ΡΠΏΠΈΡΠΊΠΎΠ²
- ΠΡΡΠΏΠΏΠΈΡΡΠΉΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ (batch requests)
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ debounce/throttle Π΄Π»Ρ ΡΠ°ΡΡΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΉ
- ΠΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΠΉΡΠ΅ 429 ΠΎΡΠΈΠ±ΠΊΠΈ gracefully Ρ retry logic
- ΠΠ΅ Π΄Π΅Π»Π°ΠΉΡΠ΅ Π·Π°ΠΏΡΠΎΡΡ Π² ΡΠΈΠΊΠ»Π°Ρ Π±Π΅Π· Π·Π°Π΄Π΅ΡΠΆΠ΅ΠΊ