应使用 composer require php-backoff/backoff 安装,构造 ExponentialBackoff 必须传 $maxRetries 和 $baseDelay(毫秒),启用 jitter 需传 third true;execute() 仅响应 throw 的 Throwable,不可返回 false 或静默吞错;避免与 Guzzle retry middleware 混用。

composer require 装不上 php-backoff?先确认包名和源
官方包名是 php-backoff/backoff,不是 php-backoff 或 backoff。直接运行 composer require php-backoff/backoff 即可安装。如果失败,大概率是网络问题或 Packagist 镜像未同步——国内用户建议临时切回官方源:composer config -g repo.packagist composer https://packagist.org,再重试。
初始化 ExponentialBackoff 时别漏掉 $maxRetries 和 $baseDelay
这个库核心类是 ExponentialBackoff,构造函数必须传两个参数:$maxRetries(最大重试次数)和 $baseDelay(基础延迟毫秒数)。漏掉任一参数会抛 TypeError。注意:$baseDelay 单位是毫秒,不是秒;且默认不带 jitter(抖动),若需防雪崩,得手动传第 3 个参数 true 启用随机因子。
常见写法:
use Backoff\ExponentialBackoff; $backoff = new ExponentialBackoff(3, 100); // 最多重试 3 次,初始延迟 100ms // 或启用 jitter: $backoff = new ExponentialBackoff(3, 100, true);
execute() 回调里 throw 的异常必须被 catch,否则重试失效
execute() 方法只重试那些明确抛出异常的回调。如果回调里用了 return false、exit 或静默吞掉错误,退避逻辑根本不会触发。它只对 Throwable 响应,且默认重试所有异常——如果你只想重试特定类型(比如 HttpException),得自己包装一层判断逻辑。
立即学习“PHP免费学习笔记(深入)”;
- ✅ 正确:回调内主动
throw new RuntimeException('network failed') - ❌ 错误:回调返回
false,或捕获异常后只打日志不 re-throw - ⚠️ 注意:PHP 7.4+ 支持
throw表达式,但execute()不识别,仍需语句式throw
配合 Guzzle HTTP 客户端时,别在 retry middleware 里重复退避
如果你用的是 Guzzle,它自带 RetryMiddleware,和 php-backoff 功能重叠。两者混用会导致延迟叠加、重试次数翻倍甚至死循环。推荐方案:要么全用 Guzzle 自带重试(配置 retry_delay 和 retry_on_status),要么关掉 Guzzle 的 retry,把整个请求逻辑包进 ExponentialBackoff::execute()。
关掉 Guzzle retry 的关键配置项是:'http_errors' => false + 手动检查响应状态码,再决定是否 throw ——因为 http_errors => true 会自动抛异常,但可能不符合你的业务重试条件(比如 404 不该重试)。
真正容易被忽略的点是:退避策略生效的前提,是你能准确区分“暂时性失败”和“永久性错误”。比如数据库唯一约束冲突,重试一百次也没用;而 DNS 解析超时,才值得指数退避。别让 execute() 变成盲目 retry 的黑盒。



















