本文目录导读:

在Python中实现接口请求的失败重试,主要有以下几种常见方案:
使用 requests + urllib3 的重试机制
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_retry_session(retries=3, backoff_factor=0.5):
"""
创建带重试机制的请求会话
retries: 重试次数
backoff_factor: 退避因子(每次重试等待时间 = backoff_factor * (2 ** (重试次数-1)))
"""
session = requests.Session()
retry_strategy = Retry(
total=retries,
read=retries,
connect=retries,
backoff_factor=backoff_factor,
status_forcelist=[500, 502, 503, 504], # 需要重试的HTTP状态码
allowed_methods=["GET", "POST", "PUT", "DELETE"], # 允许重试的HTTP方法
raise_on_status=True # 超过重试次数后抛出异常
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
return session
# 使用示例
def fetch_with_retry(url, headers=None, timeout=10):
session = create_retry_session(retries=3, backoff_factor=1)
try:
response = session.get(url, headers=headers, timeout=timeout)
response.raise_for_status() # 检查请求是否成功
return response.json()
except requests.exceptions.RequestException as e:
print(f"请求失败,已重试3次: {e}")
return None
finally:
session.close()
# 测试
if __name__ == "__main__":
# 模拟一个可能失败的服务
result = fetch_with_retry("https://api.example.com/data")
print(result)
使用 tenacity 库(更灵活的重试方案)
import requests
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
@retry(
stop=stop_after_attempt(3), # 最多重试3次
wait=wait_exponential(multiplier=1, min=1, max=10), # 指数退避等待
retry=retry_if_exception_type((requests.ConnectionError,
requests.Timeout,
requests.HTTPError)) # 只在这些异常时重试
)
def fetch_data_with_tenacity(url):
"""使用tenacity的重试装饰器"""
response = requests.get(url, timeout=5)
response.raise_for_status() # 如果HTTP状态码>=400则抛出异常
return response.json()
# 更复杂的重试逻辑
from tenacity import before_log, after_log, before_sleep_log
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@retry(
stop=stop_after_attempt(5),
wait=wait_exponential(multiplier=1, min=2, max=30),
retry=retry_if_exception_type((requests.exceptions.RequestException,)),
before=before_log(logger, logging.INFO),
after=after_log(logger, logging.INFO),
before_sleep=before_sleep_log(logger, logging.INFO),
reraise=True # 重试结束后重新抛出异常
)
def fetch_with_logging(url):
"""带日志记录的重试请求"""
response = requests.get(url, timeout=5)
response.raise_for_status()
data = response.json()
return data
# 使用示例
if __name__ == "__main__":
try:
result = fetch_with_logging("https://api.example.com/data")
print(result)
except Exception as e:
print(f"最终请求失败: {e}")
手动实现重试机制(自定义控制)
import requests
import time
from typing import Callable, Any, Optional
def retry_request(
func: Callable,
max_retries: int = 3,
backoff_factor: float = 0.5,
exceptions: tuple = (requests.exceptions.RequestException,),
on_retry: Optional[Callable] = None
) -> Any:
"""
通用的重试装饰器
func: 要重试的函数
max_retries: 最大重试次数
backoff_factor: 退避因子
exceptions: 需要重试的异常类型
on_retry: 重试时的回调函数
"""
for attempt in range(max_retries + 1):
try:
return func()
except exceptions as e:
if attempt == max_retries:
raise # 最后一次尝试失败,抛出异常
wait_time = backoff_factor * (2 ** attempt)
if on_retry:
on_retry(attempt + 1, max_retries, e, wait_time)
else:
print(f"重试 {attempt + 1}/{max_retries}, 等待 {wait_time:.2f}秒, 错误: {e}")
time.sleep(wait_time)
# 自定义重试回调
def custom_on_retry(attempt, max_retries, error, wait_time):
"""自定义重试时的行为"""
print(f"[{attempt}/{max_retries}] 请求失败: {error}")
print(f"等待 {wait_time:.2f}秒后重试...")
# 使用示例
def make_request():
"""要重试的实际请求函数"""
response = requests.get("https://api.example.com/data", timeout=5)
response.raise_for_status()
return response.json()
# 执行带重试的请求
if __name__ == "__main__":
try:
result = retry_request(
make_request,
max_retries=3,
backoff_factor=1,
on_retry=custom_on_retry
)
print(f"请求成功: {result}")
except Exception as e:
print(f"最终请求失败: {e}")
完整的重试案例(包含所有常见场景)
import requests
import time
import logging
from functools import wraps
from typing import Union, Dict, Optional
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class RetryStrategy:
"""重试策略配置"""
def __init__(
self,
max_retries: int = 3,
backoff_factor: float = 0.5,
max_backoff: float = 30,
retry_on_status: list = None,
retry_on_exceptions: tuple = None,
allowed_methods: list = None
):
self.max_retries = max_retries
self.backoff_factor = backoff_factor
self.max_backoff = max_backoff
self.retry_on_status = retry_on_status or [500, 502, 503, 504]
self.retry_on_exceptions = retry_on_exceptions or (
requests.ConnectionError,
requests.Timeout,
requests.HTTPError
)
self.allowed_methods = allowed_methods or ["GET", "POST", "PUT", "DELETE"]
def retry_decorator(strategy: RetryStrategy = None):
"""重试装饰器"""
if strategy is None:
strategy = RetryStrategy()
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(strategy.max_retries + 1):
try:
response = func(*args, **kwargs)
# 检查HTTP状态码
if response.status_code in strategy.retry_on_status:
if attempt == strategy.max_retries:
response.raise_for_status()
raise requests.HTTPError(
f"HTTP {response.status_code}",
response=response
)
return response
except strategy.retry_on_exceptions as e:
last_exception = e
if attempt == strategy.max_retries:
logger.error(f"达到最大重试次数 {strategy.max_retries}, 请求失败")
raise
# 计算等待时间(指数退避)
wait_time = min(
strategy.backoff_factor * (2 ** attempt),
strategy.max_backoff
)
logger.warning(
f"请求失败 (尝试 {attempt + 1}/{strategy.max_retries}), "
f"等待 {wait_time:.2f}秒, 错误: {type(e).__name__}: {e}"
)
time.sleep(wait_time)
# 不应该执行到这里,但为了安全返回
if last_exception:
raise last_exception
return wrapper
return decorator
# 使用装饰器的示例
@retry_decorator()
def fetch_data(url: str, headers: Dict = None, timeout: int = 10) -> requests.Response:
"""获取数据(带重试机制)"""
logger.info(f"请求 URL: {url}")
response = requests.get(url, headers=headers, timeout=timeout)
response.raise_for_status()
return response
# 更具体的配置
custom_strategy = RetryStrategy(
max_retries=5,
backoff_factor=1,
max_backoff=60,
retry_on_status=[429, 500, 502, 503, 504], # 包含429限流状态码
retry_on_exceptions=(requests.ConnectionError, requests.Timeout)
)
@retry_decorator(custom_strategy)
def fetch_with_custom_strategy(url: str) -> requests.Response:
"""使用自定义策略的请求"""
response = requests.get(url, timeout=5)
response.raise_for_status()
return response
# 测试用例
if __name__ == "__main__":
# 测试正常的请求
try:
response = fetch_data("http://httpbin.org/get")
print(f"成功获取数据: {response.status_code}")
except Exception as e:
print(f"请求失败: {e}")
# 测试模拟失败的服务
try:
response = fetch_with_custom_strategy("http://httpbin.org/status/500")
print(f"状态码: {response.status_code}")
except Exception as e:
print(f"请求最终失败: {e}")
# 测试超时重试
try:
response = fetch_data("http://httpbin.org/delay/10", timeout=3)
except requests.Timeout:
print("请求超时并重试后仍然失败")
重试策略建议
何时使用哪种方案:
- 简单场景:使用
requests+urllib3的重试机制 - 需要灵活控制:使用
tenacity库 - 需要完全控制:手动实现重试逻辑
重试注意事项:
- 幂等性:确保重试的请求是幂等的(不会产生副作用)
- 超时设置:合理设置请求超时时间
- 退避策略:使用指数退避避免雪崩效应
- 日志记录:记录重试过程便于排查问题
- 限流保护:对429状态码也要考虑重试
最佳实践:
# 建议使用的完整配置
retry_config = {
'max_retries': 3, # 最多重试3次
'backoff_factor': 0.5, # 退避因子
'max_backoff': 10, # 最大等待时间
'status_forcelist': [429, 500, 502, 503, 504],
'allowed_methods': ['GET', 'HEAD', 'OPTIONS'], # 只对幂等方法重试
'raise_on_status': True # 超过重试次数后抛出异常
}
这些案例涵盖了Python中常见的接口重试实现方式,你可以根据实际需求选择最适合的方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播