API 网关优雅降级与熔断机制:基于 Netflix Hystrix 模型的 TypeScript 实战
在大型分布式系统与 Web3 API 架构中,下游依赖(Downstream Dependencies)往往存在不可控的波动:
- 第三方以太坊 JSON-RPC 节点(如 Infura / Alchemy)突发 504 网关超时;
- 链上价格预言机(Chainlink / Coingecko)接口抖动;
- 内部大语言模型 GPU 推理微服务排队阻塞。
如果没有熔断与降级机制,单个下游慢依赖会在几秒钟之内霸占整个 API 网关的全部 TCP 连接池与工作线程,引发致命的**“级联雪崩(Cascading Failure)”**,导致全站所有原本正常的业务一同瘫痪。
熔断器模式(Circuit Breaker Pattern,源自 Netflix Hystrix 工业模型)通过有限状态机隔离不健康的依赖。
本文拆解如何在 Node.js / TypeScript 环境下构建一套支持闭合(Closed)、断开(Open)、半开(Half-Open)三态流转与优雅降级回退(Fallback)的生产级熔断器。
一、Hystrix 熔断器三态状态机转移拓扑
stateDiagram-v2 [*] --> Closed: 初始状态 (正常服务) Closed --> Open: 连续失败率超过阈值 (如 50% 错误 或 超时) note right of Open: 熔断开启! 接下来 10 秒内所有请求立即快速失败或执行 Fallback,绝不调用下游! Open --> HalfOpen: 冷却时间结束 (Sleep Window 10s 到期) note right of HalfOpen: 允许极少量的探测请求 (Canary Request) 通行 HalfOpen --> Closed: 探测请求连续成功 HalfOpen --> Open: 探测请求再次失败 (重新进入冷却)二、TypeScript 生产级熔断器(Circuit Breaker)完整实现
// resilience/circuitBreaker.ts export type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN'; export interface CircuitBreakerOptions { failureThresholdPercentage: number; // 失败率阈值 (例如 50%) minRequestsThreshold: number; // 滑动窗口内最小样本数 (例如 10 次) sleepWindowMs: number; // 熔断后冷却等待时间 (例如 10000ms) timeoutMs: number; // 单次调用超时硬限制 (例如 2000ms) } export class CircuitBreaker<T> { private state: CircuitState = 'CLOSED'; private failureCount = 0; private successCount = 0; private totalCount = 0; private nextAttemptTimestamp = 0; private options: CircuitBreakerOptions; private fallbackFn?: () => Promise<T> | T; constructor(options: Partial<CircuitBreakerOptions> = {}, fallback?: () => Promise<T> | T) { this.options = { failureThresholdPercentage: 50, minRequestsThreshold: 10, sleepWindowMs: 10000, timeoutMs: 2500, ...options, }; this.fallbackFn = fallback; } // 包装外部不可信调用 public async execute(action: () => Promise<T>): Promise<T> { const now = Date.now(); // 1. 检查当前是否处于熔断 OPEN 状态 if (this.state === 'OPEN') { if (now >= this.nextAttemptTimestamp) { console.log('🔄 [Circuit Half-Open] Testing downstream with canary probe...'); this.state = 'HALF_OPEN'; } else { // 快速失败并执行降级逻辑 return this.triggerFallback('Circuit is OPEN (Fast Fallback)'); } } // 2. 带超时的执行调用 try { const result = await this.executeWithTimeout(action, this.options.timeoutMs); this.onSuccess(); return result; } catch (err: any) { this.onFailure(err); return this.triggerFallback(err.message); } } private async executeWithTimeout(action: () => Promise<T>, timeoutMs: number): Promise<T> { return Promise.race([ action(), new Promise<never>((_, reject) => setTimeout(() => reject(new Error(`Call timed out after ${timeoutMs}ms`)), timeoutMs) ), ]); } private onSuccess() { if (this.state === 'HALF_OPEN') { console.log('✅ [Circuit Closed] Canary request succeeded. Service restored.'); this.state = 'CLOSED'; this.resetStats(); } } private onFailure(err: Error) { this.failureCount++; this.totalCount++; if (this.state === 'HALF_OPEN') { console.warn('❌ [Circuit Re-Opened] Canary probe failed. Re-entering sleep.'); this.tripCircuit(); return; } // 检查是否满足熔断触发条件 if (this.totalCount >= this.options.minRequestsThreshold) { const errorRate = (this.failureCount / this.totalCount) * 100; if (errorRate >= this.options.failureThresholdPercentage) { console.error(`🚨 [CIRCUIT BREAKER TRIPPED] Error rate ${errorRate.toFixed(1)}% exceeded threshold. Tripping circuit!`); this.tripCircuit(); } } } private tripCircuit() { this.state = 'OPEN'; this.nextAttemptTimestamp = Date.now() + this.options.sleepWindowMs; this.resetStats(); } private resetStats() { this.failureCount = 0; this.successCount = 0; this.totalCount = 0; } private triggerFallback(reason: string): Promise<T> | T { if (this.fallbackFn) { console.warn(`🛡️ [Fallback Executed] Reason: ${reason}`); return this.fallbackFn(); } throw new Error(`CircuitBreaker execution failed: ${reason}`); } }三、实战:保护易抖动的价格预言机与优雅降级
// services/oracleService.ts import { CircuitBreaker } from '../resilience/circuitBreaker'; import { redis } from '@/lib/redis'; // 注入优雅降级策略:当下游第三方 API 崩溃或熔断时,自动回退读取 Redis 中最近的历史缓存价 const ethPriceBreaker = new CircuitBreaker<number>( { failureThresholdPercentage: 40, timeoutMs: 1500, sleepWindowMs: 8000 }, async () => { const cachedPrice = await redis.get('last_safe_eth_price'); return cachedPrice ? parseFloat(cachedPrice) : 2600.0; // 兜底返回历史安全价 } ); export async function fetchLiveETHPrice(): Promise<number> { return ethPriceBreaker.execute(async () => { const res = await fetch('https://api.third-party-oracle.com/eth-price'); if (!res.ok) throw new Error(`HTTP Error: ${res.status}`); const data = await res.json(); // 更新 Redis 缓存 await redis.set('last_safe_eth_price', data.price, 'EX', 3600); return data.price; }); }四、生产级降级三种经典策略
- 缓存回退(Stale-While-Revalidate Fallback):读取最近一次成功写入的本地内存或 Redis 缓存,对用户展示带“⚠️ 数据可能存在轻微延迟”标识的旧数据,体验远胜于直接报 500 崩溃;
- 静态兜底(Static Default):对于非核心推荐列表或广告位,直接返回空数组
[]或默认通用推荐,保证主流程畅通; - 功能静默关闭(Feature Flag Shunting):在 AI 智能扩写微服务熔断期间,前端自动将“一键润色”按钮置灰,避免用户频繁点击报错。
熔断与降级机制是分布式系统在面对外部不可靠环境时的防身利刃。学会“体面地失败”,才能交付出真正坚不可摧的工业级应用。