retry_helper.py
8.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
"""
重试机制工具模块
提供通用的网络请求重试功能,增强系统健壮性
"""
import time
import logging
from functools import wraps
from typing import Callable, Any, Union, List, Type
import requests
from openai import OpenAI
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class RetryConfig:
"""重试配置类"""
def __init__(
self,
max_retries: int = 3,
initial_delay: float = 1.0,
backoff_factor: float = 2.0,
max_delay: float = 60.0,
retry_on_exceptions: tuple = None
):
"""
初始化重试配置
Args:
max_retries: 最大重试次数
initial_delay: 初始延迟秒数
backoff_factor: 退避因子(每次重试延迟翻倍)
max_delay: 最大延迟秒数
retry_on_exceptions: 需要重试的异常类型元组
"""
self.max_retries = max_retries
self.initial_delay = initial_delay
self.backoff_factor = backoff_factor
self.max_delay = max_delay
# 默认需要重试的异常类型
if retry_on_exceptions is None:
self.retry_on_exceptions = (
requests.exceptions.RequestException,
requests.exceptions.ConnectionError,
requests.exceptions.HTTPError,
requests.exceptions.Timeout,
requests.exceptions.TooManyRedirects,
ConnectionError,
TimeoutError,
Exception # OpenAI和其他API可能抛出的一般异常
)
else:
self.retry_on_exceptions = retry_on_exceptions
# 默认配置
DEFAULT_RETRY_CONFIG = RetryConfig()
def with_retry(config: RetryConfig = None):
"""
重试装饰器
Args:
config: 重试配置,如果不提供则使用默认配置
Returns:
装饰器函数
"""
if config is None:
config = DEFAULT_RETRY_CONFIG
def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
last_exception = None
for attempt in range(config.max_retries + 1): # +1 因为第一次不算重试
try:
result = func(*args, **kwargs)
if attempt > 0:
logger.info(f"函数 {func.__name__} 在第 {attempt + 1} 次尝试后成功")
return result
except config.retry_on_exceptions as e:
last_exception = e
if attempt == config.max_retries:
# 最后一次尝试也失败了
logger.error(f"函数 {func.__name__} 在 {config.max_retries + 1} 次尝试后仍然失败")
logger.error(f"最终错误: {str(e)}")
raise e
# 计算延迟时间
delay = min(
config.initial_delay * (config.backoff_factor ** attempt),
config.max_delay
)
logger.warning(f"函数 {func.__name__} 第 {attempt + 1} 次尝试失败: {str(e)}")
logger.info(f"将在 {delay:.1f} 秒后进行第 {attempt + 2} 次尝试...")
time.sleep(delay)
except Exception as e:
# 不在重试列表中的异常,直接抛出
logger.error(f"函数 {func.__name__} 遇到不可重试的异常: {str(e)}")
raise e
# 这里不应该到达,但作为安全网
if last_exception:
raise last_exception
return wrapper
return decorator
def retry_on_network_error(
max_retries: int = 3,
initial_delay: float = 1.0,
backoff_factor: float = 2.0
):
"""
专门用于网络错误的重试装饰器(简化版)
Args:
max_retries: 最大重试次数
initial_delay: 初始延迟秒数
backoff_factor: 退避因子
Returns:
装饰器函数
"""
config = RetryConfig(
max_retries=max_retries,
initial_delay=initial_delay,
backoff_factor=backoff_factor
)
return with_retry(config)
class RetryableError(Exception):
"""自定义的可重试异常"""
pass
def with_graceful_retry(config: RetryConfig = None, default_return=None):
"""
优雅重试装饰器 - 用于非关键API调用
失败后不会抛出异常,而是返回默认值,保证系统继续运行
Args:
config: 重试配置,如果不提供则使用默认配置
default_return: 所有重试失败后返回的默认值
Returns:
装饰器函数
"""
if config is None:
config = SEARCH_API_RETRY_CONFIG
def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
last_exception = None
for attempt in range(config.max_retries + 1): # +1 因为第一次不算重试
try:
result = func(*args, **kwargs)
if attempt > 0:
logger.info(f"非关键API {func.__name__} 在第 {attempt + 1} 次尝试后成功")
return result
except config.retry_on_exceptions as e:
last_exception = e
if attempt == config.max_retries:
# 最后一次尝试也失败了,返回默认值而不抛出异常
logger.warning(f"非关键API {func.__name__} 在 {config.max_retries + 1} 次尝试后仍然失败")
logger.warning(f"最终错误: {str(e)}")
logger.info(f"返回默认值以保证系统继续运行: {default_return}")
return default_return
# 计算延迟时间
delay = min(
config.initial_delay * (config.backoff_factor ** attempt),
config.max_delay
)
logger.warning(f"非关键API {func.__name__} 第 {attempt + 1} 次尝试失败: {str(e)}")
logger.info(f"将在 {delay:.1f} 秒后进行第 {attempt + 2} 次尝试...")
time.sleep(delay)
except Exception as e:
# 不在重试列表中的异常,返回默认值
logger.warning(f"非关键API {func.__name__} 遇到不可重试的异常: {str(e)}")
logger.info(f"返回默认值以保证系统继续运行: {default_return}")
return default_return
# 这里不应该到达,但作为安全网
return default_return
return wrapper
return decorator
def make_retryable_request(
request_func: Callable,
*args,
max_retries: int = 5,
**kwargs
) -> Any:
"""
直接执行可重试的请求(不使用装饰器)
Args:
request_func: 要执行的请求函数
*args: 传递给请求函数的位置参数
max_retries: 最大重试次数
**kwargs: 传递给请求函数的关键字参数
Returns:
请求函数的返回值
"""
config = RetryConfig(max_retries=max_retries)
@with_retry(config)
def _execute():
return request_func(*args, **kwargs)
return _execute()
# 预定义一些常用的重试配置
LLM_RETRY_CONFIG = RetryConfig(
max_retries=5, # 增加到5次重试
initial_delay=3.0, # 增加初始延迟到3秒
backoff_factor=1.8, # 调整退避因子
max_delay=45.0 # 增加最大延迟
)
SEARCH_API_RETRY_CONFIG = RetryConfig(
max_retries=5, # 增加到5次重试
initial_delay=2.0, # 增加初始延迟
backoff_factor=1.6, # 调整退避因子
max_delay=25.0 # 增加最大延迟
)
DB_RETRY_CONFIG = RetryConfig(
max_retries=5, # 增加到5次重试
initial_delay=1.0, # 保持较短的数据库重试延迟
backoff_factor=1.5,
max_delay=10.0
)