guzzle 默认 retry 不处理 429,因 decider 未包含该状态码;需自定义 decider 判断 429,并在 delay 中解析 retry-after(支持秒数或 http-date),结合指数退避实现优雅限流重试。

Hyperf 里用 Guzzle 做第三方 API 调用时,光配 retry 中间件不够——它只管失败重试,不管限流触发后的等待与节流。真正的“优雅”,是让请求在被服务端返回 429 Too Many Requests 后,自动解析 Retry-After 头、或按指数退避延迟重试,而不是无脑冲。
为什么 Guzzle 的默认 retry 不处理 429?
Guzzle 的 RetryMiddleware 默认只对网络异常(如 ConnectException)和 5xx 响应重试,429 被当成“合法失败响应”直接透出,不会进重试逻辑。你看到的请求瞬间失败、没延迟、没退避,就是这个原因。
- 检查源码可知:
RetryMiddleware的默认decider函数不包含对429状态码的判断 -
httpErrors中间件若启用,会提前把429转成异常,但RetryMiddleware默认也不重试这类异常(除非你显式配置) - 即使手动加了
429判断,Guzzle 本身不解析Retry-After,得自己从响应头读取并转成毫秒延迟
如何让 retry 中间件识别 429 并动态延迟?
你需要自定义 decider 和 delay 函数,并确保它们能访问到响应对象(或异常里的响应)。
- 用
Middleware::retry()创建中间件时,传入闭包形式的decider:检测$reason是否为RequestException,再判断$reason->getResponse()->getStatusCode() === 429 -
delay函数不能只写exponentialDelay;要优先读$reason->getResponse()->getHeaderLine('Retry-After'),若存在且为数字,直接返回(int)$header * 1000;否则 fallback 到指数退避 - 注意:Hyperf 的
GuzzleClient实例通常由Factory创建,中间件需注入到HandlerStack,而非仅传给单次request()
示例关键片段:
$decider = function ($retries, $request, $response, $exception) {
if ($exception instanceof RequestException && $exception->getResponse()) {
return $exception->getResponse()->getStatusCode() === 429;
}
return $retries getStatusCode() >= 500);
};
<p>$delay = function ($retries, $request, $response, $exception) {
if ($exception instanceof RequestException && $exception->getResponse()) {
$retryAfter = $exception->getResponse()->getHeaderLine('Retry-After');
if (is_numeric($retryAfter)) {
return (int)$retryAfter <em> 1000;
}
}
return (int)pow(2, $retries - 1) </em> 1000;
};</p><p>$stack = HandlerStack::create();
$stack->push(Middleware::retry($decider, $delay));
</p>
Hyperf 下如何复用 Guzzle 配置并避免中间件污染?
Hyperf 的 Guzzle 客户端支持通过 config/autoload/guzzle.php 全局配置 handler,但直接改这个文件会让所有 Guzzle 实例共用同一套限流逻辑——这在多租户或对接多个第三方 API 时很危险。
- 推荐做法:为不同 API 创建独立的
ClientFactory,每个 factory 返回带专属HandlerStack的 client - 不要在
guzzle.php里硬编码RetryMiddleware,而是用 DI 注入方式,在需要的地方按需构造带限流的 client - 如果用了
hamburgscleanest/guzzle-advanced-throttle这类高级限流包,它的ThrottleMiddleware本身已内置429处理,但要注意它和RetryMiddleware的执行顺序——ThrottleMiddleware应放在更外层,先做主动限速;RetryMiddleware放内层,专做兜底重试
真正容易被忽略的是:429 响应里 Retry-After 可能是秒级整数,也可能是 HTTP-date 字符串(如 Mon, 19 May 2026 02:35:00 GMT),后者必须用 strtotime() 解析并换算成相对毫秒,否则延迟会错乱。别假设它永远是数字。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











