import requests from requests.exceptions import RequestException import time from typing import Optional, Dict, Any, Tuple, Union def send_request( method: str, url: str, headers: Optional[Dict[str, str]] = None, data: Optional[Union[Dict[str, Any], str]] = None, files: Optional[Dict[str, Any]] = None, timeout: int = 30, retries: int = 3, backoff_factor: float = 0.5, retry_on_status: Optional[Tuple[int, ...]] = None ) -> requests.Response: """ 发送HTTP请求,支持重试机制 参数: method: 请求方法,支持"GET"或"POST" url: 请求URL headers: 请求头字典 data: 请求数据,可以是字典或字符串 files: 上传文件字典,格式为{'file': open('path', 'rb')} timeout: 请求超时时间(秒) retries: 失败重试次数 backoff_factor: 重试间隔退避因子(秒) retry_on_status: 需要重试的HTTP状态码元组,如(500, 502, 503) 返回: requests.Response: 响应对象 抛出: RequestException: 所有重试后仍然失败时抛出 """ # 规范化方法名称 method = method.upper() # 验证方法是否支持 if method not in ["GET", "POST"]: raise ValueError(f"Unsupported HTTP method: {method}") # 初始化重试计数器 retry_attempts = 0 # 主循环:处理重试逻辑 while True: try: # 根据请求方法调用对应的requests函数 if method == "GET": response = requests.get( url=url, headers=headers, timeout=timeout ) else: # POST response = requests.post( url=url, headers=headers, data=data, files=files, timeout=timeout ) # 检查状态码是否需要重试 if retry_on_status and response.status_code in retry_on_status: raise RequestException(f"Server returned retryable status code: {response.status_code}") # 状态码正常,返回响应 return response except RequestException as e: # 重试次数耗尽,抛出异常 if retry_attempts >= retries: raise RequestException(f"All {retries} retry attempts failed: {str(e)}") from e # 计算退避时间:backoff_factor * (2 ** (retry_attempts)) wait_time = backoff_factor * (2 ** retry_attempts) print(f"Request failed (attempt {retry_attempts + 1}/{retries}): {str(e)}") print(f"Retrying in {wait_time:.2f} seconds...") # 等待后重试 time.sleep(wait_time) retry_attempts += 1