// 简易熔断器:连续失败 N 次后,在冷却期内直接抛错 const circuitState = { failures: 0, openUntil: 0 }; const CIRCUIT_THRESHOLD = 5; // 连续失败 5 次触发熔断 const CIRCUIT_COOLDOWN_MS = 60000; // 熔断冷却 60 秒 function checkCircuit(label) { if (Date.now() < circuitState.openUntil) { throw new Error(`[circuit-breaker] ${label} 熔断中,${Math.ceil((circuitState.openUntil - Date.now()) / 1000)}s 后恢复`); } } function recordSuccess() { circuitState.failures = 0; circuitState.openUntil = 0; } function recordFailure(label) { circuitState.failures++; if (circuitState.failures >= CIRCUIT_THRESHOLD) { circuitState.openUntil = Date.now() + CIRCUIT_COOLDOWN_MS; console.warn(` 🔌 [circuit-breaker] ${label} 连续失败 ${circuitState.failures} 次,熔断 ${CIRCUIT_COOLDOWN_MS / 1000}s`); } } async function withRetry(fn, { maxRetries = 3, baseDelay = 1000, label = "operation" } = {}) { checkCircuit(label); let lastError; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const result = await fn(); recordSuccess(); return result; } catch (error) { lastError = error; recordFailure(label); if (attempt < maxRetries) { // 指数退避 + 随机抖动(避免雷群效应) const jitter = Math.random() * baseDelay * 0.5; const delay = baseDelay * Math.pow(2, attempt - 1) + jitter; console.warn(` ⚠ ${label} 第${attempt}次失败,${(delay / 1000).toFixed(1)}s后重试...`); await new Promise((resolve) => setTimeout(resolve, delay)); // 每次重试前检查熔断 checkCircuit(label); } } } throw lastError; } export { withRetry };