Files
astrbot_plugin_qq_group_dai…/src/infrastructure/resilience/retry.py
T
2026-02-08 23:28:56 +08:00

177 lines
4.6 KiB
Python

"""
重试 - 带指数退避的重试工具
提供用于处理瞬态故障的重试装饰器和工具。
"""
import asyncio
import random
from dataclasses import dataclass
from functools import wraps
from typing import Callable, Optional, Tuple, Type, Union
from ...utils.logger import logger
@dataclass
class RetryConfig:
"""重试行为配置。"""
max_attempts: int = 3
base_delay: float = 1.0
max_delay: float = 60.0
exponential_base: float = 2.0
jitter: bool = True
retry_exceptions: Tuple[Type[Exception], ...] = (Exception,)
def calculate_delay(
attempt: int,
base_delay: float,
max_delay: float,
exponential_base: float,
jitter: bool,
) -> float:
"""
计算重试尝试的延迟。
参数:
attempt: 当前尝试次数(从 0 开始)
base_delay: 基础延迟(秒)
max_delay: 最大延迟(秒)
exponential_base: 指数退避的基数
jitter: 是否添加随机抖动
返回:
延迟时间(秒)
"""
delay = base_delay * (exponential_base**attempt)
delay = min(delay, max_delay)
if jitter:
delay = delay * (0.5 + random.random())
return delay
def retry_async(
max_attempts: int = 3,
base_delay: float = 1.0,
max_delay: float = 60.0,
exponential_base: float = 2.0,
jitter: bool = True,
retry_exceptions: Tuple[Type[Exception], ...] = (Exception,),
on_retry: Optional[Callable[[Exception, int], None]] = None,
):
"""
带指数退避的异步函数重试装饰器。
参数:
max_attempts: 最大尝试次数
base_delay: 重试之间的基础延迟
max_delay: 重试之间的最大延迟
exponential_base: 指数退避的基数
jitter: 是否添加随机抖动
retry_exceptions: 要重试的异常元组
on_retry: 重试时的可选回调(异常,尝试次数)
返回:
装饰后的函数
"""
def decorator(func: Callable):
@wraps(func)
async def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(max_attempts):
try:
return await func(*args, **kwargs)
except retry_exceptions as e:
last_exception = e
if attempt < max_attempts - 1:
delay = calculate_delay(
attempt, base_delay, max_delay, exponential_base, jitter
)
if on_retry:
on_retry(e, attempt + 1)
logger.debug(
f"重试 {attempt + 1}/{max_attempts} {func.__name__} "
f"延迟 {delay:.2f}s: {e}"
)
await asyncio.sleep(delay)
else:
logger.warning(
f"{func.__name__} 的所有 {max_attempts} 次尝试均失败: {e}"
)
raise last_exception
return wrapper
return decorator
class RetryExecutor:
"""
带重试逻辑的函数执行器。
"""
def __init__(self, config: Optional[RetryConfig] = None):
"""
初始化重试执行器。
参数:
config: 重试配置
"""
self.config = config or RetryConfig()
async def execute(
self,
func: Callable,
*args,
config: Optional[RetryConfig] = None,
**kwargs,
):
"""
使用重试逻辑执行函数。
参数:
func: 要执行的异步函数
*args: 函数参数
config: 可选的覆盖配置
**kwargs: 函数关键字参数
返回:
函数结果
异常:
Exception: 如果所有重试都失败
"""
cfg = config or self.config
last_exception = None
for attempt in range(cfg.max_attempts):
try:
return await func(*args, **kwargs)
except cfg.retry_exceptions as e:
last_exception = e
if attempt < cfg.max_attempts - 1:
delay = calculate_delay(
attempt,
cfg.base_delay,
cfg.max_delay,
cfg.exponential_base,
cfg.jitter,
)
logger.debug(
f"重试 {attempt + 1}/{cfg.max_attempts} 延迟 {delay:.2f}s: {e}"
)
await asyncio.sleep(delay)
raise last_exception