import requests
import time
from typing import Callable, Optional
from dataclasses import dataclass
from datetime import datetime, timedelta
import math

@dataclass
class CircuitBreakerState:
    """熔断器状态"""
    failures: int = 0
    last_failure_time: Optional[datetime] = None
    is_open: bool = False
    next_attempt_time: Optional[datetime] = None

class APIClientWithRetry:
    """带重试和熔断机制的API客户端"""
    
    def __init__(
        self,
        max_retries: int = 3,
        backoff_factor: float = 0.5,
        failure_threshold: int = 5,
        reset_timeout: timedelta = timedelta(seconds=30)
    ):
        """
        参数:
            max_retries: 最大重试次数
            backoff_factor: 指数退避因子
            failure_threshold: 熔断触发阈值
            reset_timeout: 熔断恢复时间
        """
        self.max_retries = max_retries
        self.backoff_factor = backoff_factor
        self.failure_threshold = failure_threshold
        self.reset_timeout = reset_timeout
        self.circuit_breaker = CircuitBreakerState()
        self.session = requests.Session()
    
    def _calculate_backoff(self, retry_count: int) -> float:
        """计算指数退避时间"""
        return self.backoff_factor * (2 ** (retry_count - 1))
    
    def _should_retry(self, status_code: int) -> bool:
        """判断是否需要重试"""
        return status_code >= 500 or status_code == 429
    
    def _update_circuit_breaker(self, success: bool):
        """更新熔断器状态"""
        now = datetime.now()
        
        if success:
            if self.circuit_breaker.is_open:
                # 熔断状态下成功请求,关闭熔断器
                self.circuit_breaker.is_open = False
                self.circuit_breaker.failures = 0
            return
        
        # 请求失败,更新状态
        self.circuit_breaker.failures += 1
        self.circuit_breaker.last_failure_time = now
        
        # 检查是否触发熔断
        if (self.circuit_breaker.failures >= self.failure_threshold and 
            not self.circuit_breaker.is_open):
            self.circuit_breaker.is_open = True
            self.circuit_breaker.next_attempt_time = now + self.reset_timeout
    
    def request_with_retry(
        self,
        method: str,
        url: str,
        **kwargs
    ) -> requests.Response:
        """带重试和熔断机制的请求方法"""
        
        # 检查熔断状态
        if self.circuit_breaker.is_open:
            if datetime.now() < self.circuit_breaker.next_attempt_time:
                raise Exception(f"Circuit breaker open until {self.circuit_breaker.next_attempt_time}")
            # 尝试恢复
            self.circuit_breaker.is_open = False
        
        last_exception = None
        
        for attempt in range(self.max_retries + 1):
            try:
                response = self.session.request(method, url, **kwargs)
                
                if not self._should_retry(response.status_code):
                    self._update_circuit_breaker(True)
                    return response
                
                # 需要重试的情况
                if attempt == self.max_retries:
                    self._update_circuit_breaker(False)
                    return response
                
            except Exception as e:
                last_exception = e
                if attempt == self.max_retries:
                    self._update_circuit_breaker(False)
                    raise
            
            # 计算并等待退避时间
            backoff = self._calculate_backoff(attempt + 1)
            time.sleep(backoff)
        
        self._update_circuit_breaker(False)
        raise last_exception or Exception("Unknown error occurred")

# 示例使用
if __name__ == "__main__":
    client = APIClientWithRetry(
        max_retries=3,
        backoff_factor=0.5,
        failure_threshold=3,
        reset_timeout=timedelta(seconds=10)
    )
    
    try:
        response = client.request_with_retry(
            "GET",
            "https://api.example.com/data",
            timeout=5
        )
        print("请求成功:", response.status_code)
    except Exception as e:
        print("请求失败:", str(e))

使用说明

  1. ​​功能特点​​:

    • 自动重试失败的API请求(针对5xx和429状态码)
    • 指数退避算法避免请求风暴
    • 熔断机制防止连续失败
    • 自动恢复机制
    • 线程安全的请求会话
  2. ​​核心参数​​:

    • max_retries: 最大重试次数(默认3)
    • backoff_factor: 退避因子(默认0.5秒)
    • failure_threshold: 熔断触发阈值(默认5次失败)
    • reset_timeout: 熔断恢复时间(默认30秒)
  3. ​​使用方法​​:

    # 初始化客户端
    client = APIClientWithRetry(
        max_retries=3,
        backoff_factor=0.5
    )
    
    # 发送请求
    try:
        response = client.request_with_retry(
            "GET",
            "https://api.example.com/data",
            params={"key": "value"},
            headers={"Authorization": "Bearer token"}
        )
        print(response.json())
    except Exception as e:
        print("请求最终失败:", e)
  4. ​​应用场景​​:

    • 调用不稳定第三方API
    • 微服务间通信
    • 高并发请求处理
    • 需要高可靠性的API调用
  5. ​​注意事项​​:

    • 非幂等操作(POST/PUT/DELETE)慎用
    • 熔断期间所有请求会被拒绝
    • 退避时间随重试次数指数增长
    • 生产环境建议添加监控指标

更多推荐