Skip to main content
Glama
rate-limiter.ts5.75 kB
// 簡單的速率限制器實作,不依賴外部包 export class RateLimiter { private tokens: number; private lastRefill: number; private readonly maxTokens: number; private readonly refillRate: number; // tokens per second constructor(maxTokens: number, refillRate: number) { this.maxTokens = maxTokens; this.refillRate = refillRate; this.tokens = maxTokens; this.lastRefill = Date.now(); } private refill(): void { const now = Date.now(); const timePassed = (now - this.lastRefill) / 1000; const tokensToAdd = Math.floor(timePassed * this.refillRate); if (tokensToAdd > 0) { this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd); this.lastRefill = now; } } async consume(tokens: number = 1): Promise<void> { this.refill(); if (this.tokens >= tokens) { this.tokens -= tokens; return; } // 計算需要等待的時間 const tokensNeeded = tokens - this.tokens; const waitTime = (tokensNeeded / this.refillRate) * 1000; await new Promise(resolve => setTimeout(resolve, waitTime)); await this.consume(tokens); } getAvailableTokens(): number { this.refill(); return this.tokens; } } // 全域速率限制器實例 const globalRateLimiter = new RateLimiter(10, 1); // 每秒最多10個請求 // 簡單的並發限制器 export class ConcurrencyLimiter { private activeCount: number = 0; private pendingQueue: Array<() => void> = []; constructor(private maxConcurrency: number) {} async run<T>(fn: () => Promise<T>): Promise<T> { return new Promise((resolve, reject) => { const execute = async () => { this.activeCount++; try { const result = await fn(); resolve(result); } catch (error) { reject(error); } finally { this.activeCount--; this.processQueue(); } }; if (this.activeCount < this.maxConcurrency) { execute(); } else { this.pendingQueue.push(execute); } }); } private processQueue(): void { if (this.pendingQueue.length > 0 && this.activeCount < this.maxConcurrency) { const next = this.pendingQueue.shift(); if (next) { next(); } } } get active(): number { return this.activeCount; } get pending(): number { return this.pendingQueue.length; } clearQueue(): void { this.pendingQueue = []; } } // 全域並發限制器 const globalConcurrencyLimiter = new ConcurrencyLimiter(5); // 最多5個並發請求 // 組合速率限制和並發控制的函數 export async function throttledRequest<T>( fn: () => Promise<T>, options: { rateLimiter?: RateLimiter; concurrencyLimiter?: ConcurrencyLimiter; } = {} ): Promise<T> { const { rateLimiter = globalRateLimiter, concurrencyLimiter = globalConcurrencyLimiter } = options; // 先消耗速率限制令牌 await rateLimiter.consume(1); // 然後在並發限制下執行 return concurrencyLimiter.run(fn); } // 智能速率限制器 - 根據錯誤狀態自動調整 export class AdaptiveRateLimiter { private baseLimiter: RateLimiter; private errorCount: number = 0; private successCount: number = 0; private lastErrorTime: number = 0; private adaptationWindow: number = 60000; // 1分鐘 constructor( private baseRate: number, private maxTokens: number = baseRate * 2, private errorThreshold: number = 3 ) { this.baseLimiter = new RateLimiter(maxTokens, baseRate); } async consume(): Promise<void> { // 檢查是否需要調整速率 this.adjustRate(); await this.baseLimiter.consume(1); } onSuccess(): void { this.successCount++; this.errorCount = Math.max(0, this.errorCount - 0.1); // 緩慢恢復 } onError(): void { this.errorCount++; this.lastErrorTime = Date.now(); this.successCount = Math.max(0, this.successCount - 1); } private adjustRate(): void { const now = Date.now(); const timeSinceLastError = now - this.lastErrorTime; if (this.errorCount >= this.errorThreshold) { // 如果錯誤過多,降低速率 const slowdownFactor = Math.min(0.1, 1 / (this.errorCount - this.errorThreshold + 1)); const newRate = this.baseRate * slowdownFactor; this.baseLimiter = new RateLimiter(this.maxTokens, newRate); } else if (timeSinceLastError > this.adaptationWindow && this.successCount > 10) { // 如果長時間沒有錯誤且成功率高,逐漸恢復到基準速率 const recoveryFactor = Math.min(1, this.successCount / 20); const newRate = this.baseRate * recoveryFactor; this.baseLimiter = new RateLimiter(this.maxTokens, newRate); } } getStats(): { errorCount: number; successCount: number; currentRate: number; availableTokens: number; } { return { errorCount: this.errorCount, successCount: this.successCount, currentRate: (this.baseLimiter as any).refillRate, availableTokens: this.baseLimiter.getAvailableTokens() }; } } // 全域的調用時間記錄 const _lastCalls = new Map<string, number>(); // 簡單的速率限制函數 export async function rateLimit(intervalMs: number = 1000): Promise<void> { const key = 'global'; const now = Date.now(); const lastCall = _lastCalls.get(key) || 0; const timeSinceLastCall = now - lastCall; if (timeSinceLastCall < intervalMs) { const waitTime = intervalMs - timeSinceLastCall; await new Promise(resolve => setTimeout(resolve, waitTime)); } _lastCalls.set(key, Date.now()); } // 導出便利函數 export { globalRateLimiter, globalConcurrencyLimiter };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SunZhi-Will/website-to-markdown-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server