高并发系统设计-重试
一、概述
1.1 什么是重试
重试(Retry)是指:
当一次请求失败后,在满足条件的情况下再次发起请求,以提高成功率。
例如:
请求 → 失败 → 再请求 → 成功
1.2 为什么需要重试
在分布式系统中,失败是常态:
- 网络抖动
- RPC短暂超时
- Redis瞬时不可用
- MySQL连接波动
- 依赖服务重启
例如:
请求支付服务 → 超时
但实际服务是正常的
如果不重试:用户支付失败
但如果允许重试:
第一次失败
第二次成功
1.3 重试解决的问题
重试主要解决:
1. 短暂故障
如: 网络抖动 服务瞬断 连接超时
2.依赖服务不稳定
如:
第三方支付 / 短信 / API
3.提高成功率
1.4 重试的误区
错误做法 : 失败 → 立即重试 → 再失败 → 无限循环
正确做法是:可控重试 + 退避策略 + 最大次数限制
二、原理
2.1 重试的核心思想
重试本质是:
用时间换成功率
但前提是:
- 不能无限重试
- 不能立即重试
- 不能对所有错误重试
2.2 哪些情况可以重试
- 网络超时
- 连接失败
- 503 / 502
- RPC短暂异常
不可重试
- 参数错误
- 业务逻辑错误
- 权限错误
- 数据不存在
2.3 重试策略类型
1. 立即重试
失败 → 立即再试
2.固定间隔重试
1s → 1s → 1s
3. 指数退避 (核心策略,推荐)
1s → 2s → 4s → 8s → 16s
4. 随机抖动
1s + random(0~500ms)
2.4 Retry Storm(重试风暴)
需要注意的问题之一 场景:
服务A → 服务B失败
触发重试:
1000个请求 × 3次重试
= 3000请求
结果:
B 被打死
→ A继续重试
→ 系统崩溃
2.5 正确的重试模型
请求失败
↓
是否可重试?
↓
是 → 退避等待
↓
再次请求
↓
超过最大次数 → fallback
三、实现
3.1 PHP基础重试实现
function retry(callable $fn, int $maxAttempts = 3)
{
$attempt = 0;
begin:
try {
return $fn();
} catch (\Throwable $e) {
$attempt++;
if ($attempt >= $maxAttempts) {
throw $e;
}
sleep(1);
goto begin;
}
}
3.2 指数退避实现 (推荐)
function retryBackoff(callable $fn, int $maxAttempts = 3)
{
$attempt = 0;
begin:
try {
return $fn();
} catch (\Throwable $e) {
$attempt++;
if ($attempt >= $maxAttempts) {
throw $e;
}
$delay = pow(2, $attempt);
sleep($delay);
goto begin;
}
}