134 lines
3.6 KiB
Python
134 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
import time
|
|
from enum import Enum
|
|
|
|
|
|
class CircuitBreakerState(Enum):
|
|
CLOSED = "closed"
|
|
OPEN = "open"
|
|
HALF_OPEN = "half_open"
|
|
|
|
|
|
class CircuitBreaker:
|
|
"""标准断路器,冷却后自动半开。"""
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
failure_threshold: int = 5,
|
|
cooldown_seconds: float = 30.0,
|
|
) -> None:
|
|
self.name = name
|
|
self.failure_threshold = failure_threshold
|
|
self.cooldown_seconds = cooldown_seconds
|
|
self._state = CircuitBreakerState.CLOSED
|
|
self._failure_count = 0
|
|
self._last_failure_time: float = 0.0
|
|
|
|
@property
|
|
def state(self) -> CircuitBreakerState:
|
|
if (
|
|
self._state == CircuitBreakerState.OPEN
|
|
and time.monotonic() - self._last_failure_time >= self.cooldown_seconds
|
|
):
|
|
self._state = CircuitBreakerState.HALF_OPEN
|
|
return self._state
|
|
|
|
def record_success(self) -> None:
|
|
self._failure_count = 0
|
|
self._state = CircuitBreakerState.CLOSED
|
|
|
|
def record_failure(self) -> None:
|
|
self._failure_count += 1
|
|
self._last_failure_time = time.monotonic()
|
|
if self._failure_count >= self.failure_threshold:
|
|
self._state = CircuitBreakerState.OPEN
|
|
|
|
def allow_request(self) -> bool:
|
|
return self.state != CircuitBreakerState.OPEN
|
|
|
|
|
|
class RetryStrategy:
|
|
"""指数退避重试。"""
|
|
|
|
def __init__(
|
|
self,
|
|
max_retries: int = 3,
|
|
base_delay: float = 1.0,
|
|
max_delay: float = 60.0,
|
|
multiplier: float = 2.0,
|
|
) -> None:
|
|
self.max_retries = max_retries
|
|
self.base_delay = base_delay
|
|
self.max_delay = max_delay
|
|
self.multiplier = multiplier
|
|
|
|
def should_retry(self, attempt_index: int) -> bool:
|
|
return attempt_index < self.max_retries
|
|
|
|
def get_delay(self, attempt_index: int) -> float:
|
|
delay = self.base_delay * (self.multiplier**attempt_index)
|
|
return min(delay, self.max_delay)
|
|
|
|
|
|
class DowngradeAction:
|
|
"""降级链中的一个步骤。"""
|
|
|
|
def __init__(self, type: str, target: str) -> None: # noqa: A002
|
|
self.type = type # "fallback_endpoint", "fallback_model", "cache", "degrade_quality"
|
|
self.target = target
|
|
|
|
|
|
class DowngradeChain:
|
|
"""当模型/端点失败时的降级动作链。"""
|
|
|
|
def __init__(self, actions: list[DowngradeAction]) -> None:
|
|
self._actions = actions
|
|
self._index = 0
|
|
|
|
def next_action(self) -> tuple[str, str] | None:
|
|
if self._index >= len(self._actions):
|
|
return None
|
|
action = self._actions[self._index]
|
|
return (action.type, action.target)
|
|
|
|
def advance(self) -> None:
|
|
self._index += 1
|
|
|
|
def is_exhausted(self) -> bool:
|
|
return self._index >= len(self._actions)
|
|
|
|
def reset(self) -> None:
|
|
self._index = 0
|
|
|
|
|
|
class ResilienceFactory:
|
|
"""弹性组件工厂。"""
|
|
|
|
@staticmethod
|
|
def create_circuit_breaker(
|
|
name: str,
|
|
failure_threshold: int = 5,
|
|
cooldown_seconds: float = 30.0,
|
|
) -> CircuitBreaker:
|
|
return CircuitBreaker(
|
|
name=name,
|
|
failure_threshold=failure_threshold,
|
|
cooldown_seconds=cooldown_seconds,
|
|
)
|
|
|
|
@staticmethod
|
|
def create_retry_strategy(
|
|
max_retries: int = 3,
|
|
base_delay: float = 1.0,
|
|
max_delay: float = 60.0,
|
|
multiplier: float = 2.0,
|
|
) -> RetryStrategy:
|
|
return RetryStrategy(
|
|
max_retries=max_retries,
|
|
base_delay=base_delay,
|
|
max_delay=max_delay,
|
|
multiplier=multiplier,
|
|
)
|