本文目录导读:

我来介绍几种实现请求重试脚本的方法,从简单到复杂:
基础重试函数
import time
import requests
from requests.exceptions import RequestException
def request_with_retry(url, max_retries=3, delay=1):
"""
基础请求重试函数
"""
for attempt in range(max_retries):
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response
except RequestException as e:
print(f"第 {attempt + 1} 次请求失败: {e}")
if attempt < max_retries - 1:
time.sleep(delay * (attempt + 1)) # 递增延迟
else:
raise Exception("所有重试都失败")
# 使用示例
try:
result = request_with_retry("https://api.example.com/data")
print(result.json())
except Exception as e:
print(f"最终失败: {e}")
带退避策略的重试
import time
import random
from functools import wraps
def retry_with_backoff(
max_retries=3,
base_delay=1,
max_delay=60,
exponential=False,
jitter=False
):
"""
带退避策略的重试装饰器
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_retries - 1:
raise
# 计算延迟时间
if exponential:
delay = min(base_delay * (2 ** attempt), max_delay)
else:
delay = base_delay * (attempt + 1)
# 添加抖动
if jitter:
delay = delay + random.uniform(0, delay * 0.1)
print(f"重试 {attempt + 1}/{max_retries}, 等待 {delay:.2f}秒")
time.sleep(delay)
return None
return wrapper
return decorator
# 使用示例
@retry_with_backoff(max_retries=5, exponential=True, jitter=True)
def fetch_data(url):
response = requests.get(url, timeout=10)
response.raise_for_status()
return response.json()
# 调用
data = fetch_data("https://api.example.com/data")
针对HTTP状态码的重试
import time
import requests
class HTTPRetryHandler:
"""
基于HTTP状态码的请求重试
"""
RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504}
def __init__(self, max_retries=3, delay=1):
self.max_retries = max_retries
self.delay = delay
def request(self, method, url, **kwargs):
"""
执行带重试的HTTP请求
"""
for attempt in range(self.max_retries):
try:
response = requests.request(method, url, **kwargs)
# 检查是否需要重试
if response.status_code in self.RETRYABLE_STATUS_CODES:
if attempt < self.max_retries - 1:
retry_after = self._get_retry_after(response)
wait_time = retry_after or (self.delay * (attempt + 1))
print(f"状态码 {response.status_code}, 等待 {wait_time}秒后重试")
time.sleep(wait_time)
continue
response.raise_for_status()
return response
except requests.exceptions.ConnectionError:
if attempt < self.max_retries - 1:
print(f"连接错误, {self.delay * (attempt + 1)}秒后重试")
time.sleep(self.delay * (attempt + 1))
else:
raise
except requests.exceptions.Timeout:
if attempt < self.max_retries - 1:
print(f"超时错误, {self.delay * (attempt + 1)}秒后重试")
time.sleep(self.delay * (attempt + 1))
else:
raise
raise Exception(f"所有重试都失败, 最后状态码: {response.status_code}")
def _get_retry_after(self, response):
"""获取Retry-After头信息"""
retry_after = response.headers.get('Retry-After')
if retry_after:
try:
return int(retry_after)
except ValueError:
pass
return None
# 使用示例
retry_handler = HTTPRetryHandler(max_retries=5, delay=2)
response = retry_handler.request('GET', 'https://api.example.com/data')
异步重试(使用asyncio)
import asyncio
import aiohttp
import time
async def async_retry_request(url, max_retries=3, delay=1):
"""
异步请求重试
"""
async with aiohttp.ClientSession() as session:
for attempt in range(max_retries):
try:
async with session.get(url, timeout=10) as response:
if response.status == 200:
return await response.json()
elif response.status in [429, 500, 502, 503, 504]:
if attempt < max_retries - 1:
delay_time = delay * (attempt + 1)
print(f"状态码 {response.status}, 等待 {delay_time}秒")
await asyncio.sleep(delay_time)
continue
else:
response.raise_for_status()
except asyncio.TimeoutError:
if attempt < max_retries - 1:
await asyncio.sleep(delay * (attempt + 1))
else:
raise
except Exception as e:
if attempt < max_retries - 1:
print(f"错误: {e}, 重试中...")
await asyncio.sleep(delay * (attempt + 1))
else:
raise
raise Exception("所有重试都失败")
# 并行执行多个重试请求
async def main():
urls = [
"https://api1.example.com/data",
"https://api2.example.com/data",
"https://api3.example.com/data"
]
tasks = [async_retry_request(url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
for url, result in zip(urls, results):
if isinstance(result, Exception):
print(f"{url} 失败: {result}")
else:
print(f"{url} 成功: {result}")
# 运行
# asyncio.run(main())
使用第三方库(tenacity)
from tenacity import retry, stop_after_attempt, wait_exponential
import requests
# 最简单的使用方式
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10))
def make_request(url):
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.json()
# 更高级的配置
@retry(
stop=stop_after_attempt(5), # 最多重试5次
wait=wait_exponential(multiplier=2, min=1, max=60), # 指数退避
retry_error_callback=lambda retry_state: print(f"重试 {retry_state.attempt_number}") # 回调
)
def fetch_data_with_retry(url):
response = requests.get(url, timeout=10)
# 自定义重试条件
if response.status_code in [429, 503]:
raise Exception(f"Server overloaded: {response.status_code}")
response.raise_for_status()
return response.json()
# 使用
try:
data = make_request("https://api.example.com/data")
except Exception as e:
print(f"所有重试都失败: {e}")
完整的重试脚本示例
#!/usr/bin/env python3
"""
完整的请求重试脚本
支持:状态码重试、连接错误、超时、指数退避、日志记录
"""
import time
import logging
import requests
from requests.exceptions import (
RequestException,
ConnectionError,
Timeout,
HTTPError
)
from typing import Optional, Dict, Any
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class SmartRetryClient:
"""
智能重试HTTP客户端
"""
RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504, 509}
def __init__(
self,
max_retries: int = 3,
base_delay: float = 1.0,
max_delay: float = 60.0,
backoff_type: str = 'exponential',
timeout: int = 30
):
self.max_retries = max_retries
self.base_delay = base_delay
self.max_delay = max_delay
self.backoff_type = backoff_type
self.timeout = timeout
self.session = requests.Session()
def get_delay(self, attempt: int) -> float:
"""计算重试延迟"""
if self.backoff_type == 'exponential':
delay = min(self.base_delay * (2 ** attempt), self.max_delay)
elif self.backoff_type == 'linear':
delay = min(self.base_delay * (attempt + 1), self.max_delay)
else: # constant
delay = self.base_delay
return delay
def request(
self,
method: str,
url: str,
**kwargs
) -> Optional[requests.Response]:
"""
带重试的请求方法
"""
last_exception = None
for attempt in range(self.max_retries):
try:
logger.info(f"请求 {attempt + 1}/{self.max_retries}: {method} {url}")
response = self.session.request(
method,
url,
timeout=self.timeout,
**kwargs
)
# 检查状态码是否可重试
if response.status_code in self.RETRYABLE_STATUS_CODES:
if attempt < self.max_retries - 1:
retry_after = response.headers.get('Retry-After')
wait_time = float(retry_after) if retry_after else self.get_delay(attempt)
logger.warning(
f"状态码 {response.status_code}, "
f"等待 {wait_time:.2f}秒后重试"
)
time.sleep(wait_time)
continue
else:
response.raise_for_status()
# 成功响应
logger.info(f"请求成功: {response.status_code}")
return response
except (ConnectionError, Timeout) as e:
last_exception = e
if attempt < self.max_retries - 1:
delay = self.get_delay(attempt)
logger.warning(
f"连接错误: {e}, {delay:.2f}秒后重试"
)
time.sleep(delay)
else:
logger.error(f"所有重试都失败: {e}")
raise
except HTTPError as e:
if attempt < self.max_retries - 1 and e.response.status_code in self.RETRYABLE_STATUS_CODES:
delay = self.get_delay(attempt)
logger.warning(
f"HTTP错误 {e.response.status_code}, "
f"{delay:.2f}秒后重试"
)
time.sleep(delay)
else:
raise
except RequestException as e:
last_exception = e
if attempt < self.max_retries - 1:
delay = self.get_delay(attempt)
logger.warning(f"请求异常: {e}, {delay:.2f}秒后重试")
time.sleep(delay)
else:
raise
raise last_exception if last_exception else Exception("请求失败")
def get(self, url: str, **kwargs) -> Optional[requests.Response]:
return self.request('GET', url, **kwargs)
def post(self, url: str, **kwargs) -> Optional[requests.Response]:
return self.request('POST', url, **kwargs)
# 使用示例
def main():
# 创建客户端
client = SmartRetryClient(
max_retries=5,
base_delay=1,
backoff_type='exponential',
timeout=10
)
# 示例请求
try:
response = client.get("https://api.github.com")
print("状态码:", response.status_code)
print("数据:", response.json()[:100])
except Exception as e:
print(f"最终失败: {e}")
# 测试重试(使用不会响应的URL)
try:
response = client.get("https://api.example.com/nonexistent")
except Exception as e:
print(f"预期失败: {e}")
if __name__ == "__main__":
main()
选择建议
- 简单场景:使用基础的
retry_with_backoff函数 - 需要精细控制:使用
SmartRetryClient类 - 异步场景:使用asyncio版本
- 不想自己写:使用
tenacity库 - 生产环境:建议使用完整的
SmartRetryClient实现,包含日志、超时、退避策略等
选择哪种方式取决于你的具体需求和使用的技术栈。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播