| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- /**
- * 熔断器 - 供应商级别的故障隔离
- *
- * 状态机: CLOSED → (N次连续失败) → OPEN → (冷却期过后) → HALF_OPEN → (成功) → CLOSED
- *
- * 使用方式:
- * const breaker = new CircuitBreaker({ name: 'minimax', failureThreshold: 3, cooldownMs: 30000 });
- * try {
- * await breaker.call(async () => { ... });
- * } catch (e) {
- * // 熔断开启或调用失败
- * }
- */
- export type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
- export interface CircuitBreakerConfig {
- name: string;
- failureThreshold?: number; // 连续失败多少次后熔断,默认 3
- cooldownMs?: number; // 熔断冷却时间,默认 30000ms
- successThreshold?: number; // HALF_OPEN 状态下需要连续成功多少次才恢复,默认 2
- }
- export class CircuitBreaker {
- readonly name: string;
- private failureThreshold: number;
- private cooldownMs: number;
- private successThreshold: number;
- private state: CircuitState = 'CLOSED';
- private failureCount = 0;
- private successCount = 0;
- private openedAt = 0;
- constructor(config: CircuitBreakerConfig) {
- this.name = config.name;
- this.failureThreshold = config.failureThreshold ?? 3;
- this.cooldownMs = config.cooldownMs ?? 30000;
- this.successThreshold = config.successThreshold ?? 2;
- }
- getState(): CircuitState {
- // 如果 OPEN 且冷却期已过,自动进入 HALF_OPEN
- if (this.state === 'OPEN' && Date.now() - this.openedAt >= this.cooldownMs) {
- this.state = 'HALF_OPEN';
- this.failureCount = 0;
- this.successCount = 0;
- console.log(`[熔断器] ${this.name}: OPEN → HALF_OPEN(冷却期已过)`);
- }
- return this.state;
- }
- isOpen(): boolean {
- return this.getState() === 'OPEN';
- }
- /**
- * 执行受保护的操作
- * 返回操作成功的结果,抛出 CircuitBreakerOpenError 或原始错误
- */
- async call<T>(fn: () => Promise<T>): Promise<T> {
- const currentState = this.getState();
- if (currentState === 'OPEN') {
- const remainingMs = this.cooldownMs - (Date.now() - this.openedAt);
- throw new CircuitBreakerOpenError(this.name, remainingMs);
- }
- try {
- const result = await fn();
- this.onSuccess();
- return result;
- } catch (error) {
- this.onFailure();
- throw error;
- }
- }
- private onSuccess(): void {
- if (this.state === 'HALF_OPEN') {
- this.successCount++;
- if (this.successCount >= this.successThreshold) {
- this.state = 'CLOSED';
- this.failureCount = 0;
- this.successCount = 0;
- console.log(`[熔断器] ${this.name}: HALF_OPEN → CLOSED(恢复)`);
- }
- } else {
- // CLOSED 状态,重置失败计数
- this.failureCount = 0;
- }
- }
- private onFailure(): void {
- this.failureCount++;
- this.successCount = 0;
- if (this.state === 'HALF_OPEN' || (this.state === 'CLOSED' && this.failureCount >= this.failureThreshold)) {
- this.state = 'OPEN';
- this.openedAt = Date.now();
- console.log(`[熔断器] ${this.name}: → OPEN(连续失败 ${this.failureCount} 次,冷却 ${this.cooldownMs}ms)`);
- }
- }
- /** 手动重置 */
- reset(): void {
- this.state = 'CLOSED';
- this.failureCount = 0;
- this.successCount = 0;
- this.openedAt = 0;
- }
- }
- export class CircuitBreakerOpenError extends Error {
- public readonly breakerName: string;
- public readonly remainingMs: number;
- constructor(name: string, remainingMs: number) {
- super(`[${name}] 熔断器已开启,剩余冷却时间 ${Math.ceil(remainingMs / 1000)}s`);
- this.name = 'CircuitBreakerOpenError';
- this.breakerName = name;
- this.remainingMs = remainingMs;
- }
- }
|