本文目录导读:

对于脚本中灵活配置接口重试次数,有几种常见且实用的方法,我将从简单到复杂为你介绍,并提供代码示例。
环境变量配置(最灵活)
通过环境变量控制重试次数,无需修改代码。
import os
import time
import requests
from functools import wraps
# 从环境变量获取重试次数,默认3次
MAX_RETRIES = int(os.environ.get('API_RETRY_TIMES', '3'))
RETRY_DELAY = int(os.environ.get('API_RETRY_DELAY', '1')) # 秒
def retry_api_call(func):
@wraps(func)
def wrapper(*args, **kwargs):
max_retries = kwargs.pop('max_retries', MAX_RETRIES)
retry_delay = kwargs.pop('retry_delay', RETRY_DELAY)
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_retries - 1:
raise
print(f"尝试 {attempt + 1}/{max_retries} 失败: {e}")
time.sleep(retry_delay * (attempt + 1)) # 递增延迟
return wrapper
@retry_api_call
def call_api(url):
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.json()
# 使用方式
if __name__ == "__main__":
# 运行时设置:API_RETRY_TIMES=5 python script.py
result = call_api("https://api.example.com/data")
运行方式:
# 临时设置 API_RETRY_TIMES=5 API_RETRY_DELAY=2 python script.py # 或导出到环境 export API_RETRY_TIMES=3 python script.py
配置文件方式(适合复杂项目)
使用 YAML 或 JSON 配置文件。
import yaml
import json
import requests
from tenacity import retry, stop_after_attempt, wait_exponential
class APIClient:
def __init__(self, config_file='config.yaml'):
with open(config_file, 'r') as f:
self.config = yaml.safe_load(f)
self.retry_config = self.config.get('retry', {})
self.max_retries = self.retry_config.get('max_attempts', 3)
self.backoff_factor = self.retry_config.get('backoff_factor', 1)
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=1, max=10)
)
def call_api(self, url, **kwargs):
response = requests.get(url, **kwargs)
response.raise_for_status()
return response.json()
# config.yaml 示例
"""
retry:
max_attempts: 5
backoff_factor: 2
retryable_status_codes: [500, 502, 503, 504]
retryable_exceptions:
- requests.exceptions.ConnectionError
- requests.exceptions.Timeout
"""
装饰器模式(最推荐)
使用 tenacity 库实现灵活的重试策略。
import requests
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
from typing import Callable, Optional
def create_retry_decorator(
max_retries: int = 3,
min_wait: int = 1,
max_wait: int = 10,
retry_on_status: list = None
) -> Callable:
"""创建可配置的重试装饰器"""
if retry_on_status is None:
retry_on_status = [500, 502, 503, 504]
def retry_with_config(func):
@retry(
stop=stop_after_attempt(max_retries),
wait=wait_exponential(multiplier=1, min=min_wait, max=max_wait),
retry=retry_if_exception_type((
requests.exceptions.ConnectionError,
requests.exceptions.Timeout,
requests.exceptions.HTTPError
)),
before_sleep=lambda retry_state: print(
f"重试 {retry_state.attempt_number}/{max_retries}, "
f"等待 {retry_state.next_action.sleep} 秒"
)
)
def wrapper(*args, **kwargs):
response = func(*args, **kwargs)
if response.status_code in retry_on_status:
raise requests.exceptions.HTTPError(
f"状态码 {response.status_code} 需要重试"
)
return response
return wrapper
return retry_with_config
# 使用示例
@create_retry_decorator(max_retries=5, min_wait=1, max_wait=30)
def fetch_data(url):
return requests.get(url, timeout=10)
动态重试配置(最灵活)
运行时动态调整重试参数。
import requests
import time
from dataclasses import dataclass
from typing import Optional
@dataclass
class RetryConfig:
max_retries: int = 3
base_delay: float = 1.0
max_delay: float = 60.0
backoff_factor: float = 2.0
jitter: bool = True
class AdaptiveRetryClient:
def __init__(self, config: Optional[RetryConfig] = None):
self.config = config or RetryConfig()
self.retry_stats = {} # 记录重试统计数据
def call_with_retry(self, url, method='GET', **kwargs):
"""执行带重试的API调用"""
retry_count = 0
while retry_count <= self.config.max_retries:
try:
response = requests.request(method, url, **kwargs)
if response.status_code < 500:
return response
# 针对特定状态码动态调整
if response.status_code in [429]: # 限流
self.config.max_retries = min(
self.config.max_retries + 1, 10
)
except requests.exceptions.ConnectionError:
# 连接错误时增加基础延迟
self.config.base_delay *= 1.5
except requests.exceptions.Timeout:
# 超时错误时减少重试间隔
pass
retry_count += 1
delay = self._calculate_delay(retry_count)
print(f"重试 {retry_count}/{self.config.max_retries}, 等待 {delay:.1f}秒")
time.sleep(delay)
raise Exception(f"API调用失败,已重试 {self.config.max_retries} 次")
def _calculate_delay(self, attempt):
"""计算延迟时间(带指数退避和随机抖动)"""
import random
delay = min(
self.config.base_delay * (self.config.backoff_factor ** (attempt - 1)),
self.config.max_delay
)
if self.config.jitter:
delay = delay * (0.5 + random.random())
return delay
# 使用示例
client = AdaptiveRetryClient(RetryConfig(max_retries=5))
response = client.call_with_retry("https://api.example.com/data")
命令行参数方式
通过命令行参数传递重试配置。
import argparse
import requests
import sys
def main():
parser = argparse.ArgumentParser(description='API调用脚本')
parser.add_argument('--retries', type=int, default=3,
help='最大重试次数(默认:3)')
parser.add_argument('--retry-delay', type=float, default=1.0,
help='重试间隔(秒,默认:1)')
parser.add_argument('--retry-backoff', type=float, default=2.0,
help='退避因子(默认:2)')
parser.add_argument('url', help='API URL')
args = parser.parse_args()
# 调用带重试的API
for attempt in range(args.retries):
try:
response = requests.get(args.url, timeout=5)
response.raise_for_status()
print(f"成功: {response.json()}")
break
except requests.exceptions.RequestException as e:
if attempt == args.retries - 1:
print(f"最终失败: {e}")
sys.exit(1)
delay = args.retry_delay * (args.retry_backoff ** attempt)
print(f"尝试 {attempt + 1}/{args.retries} 失败, "
f"{delay:.1f}秒后重试...")
time.sleep(delay)
if __name__ == "__main__":
main()
运行方式:
python script.py --retries 5 --retry-delay 2 https://api.example.com/data
最佳实践建议
- 使用环境变量:适合不同环境(开发/测试/生产)使用不同配置
- 指数退避:避免立即重试给服务器造成压力
- 添加抖动:防止多客户端同时重试
- 日志记录:记录重试次数和原因
- 针对不同错误类型:连接错误、超时、HTTP错误使用不同策略
- 断路器模式:连续失败时可暂停重试一段时间
选择哪种方式取决于你的具体场景:
- 快速脚本:使用环境变量
- 复杂项目:使用配置文件 + tenacity
- 需要统计分析:使用动态配置方式