PHP中AI接口请求失败需自动重试,仅对连接超时、502/503/504等可恢复错误重试,采用指数退避(如100ms→200ms→400ms)、最多3次;Guzzle可通过RetryMiddleware自定义重试策略并注入HandlerStack;需用Idempotency-Key保障幂等性;务必记录重试日志并监控重试率。

PHP cURL 请求失败后怎么自动重试
网络抖动导致 AI 接口返回 CURLOPT_CONNECTTIMEOUT 超时、CURLOPT_TIMEOUT 中断、或 HTTP 状态码为 0、502、503、504 时,不能直接抛异常——得重试。PHP 本身不内置重试逻辑,必须手动封装。
核心思路:捕获 cURL 错误码 + HTTP 状态码,判断是否属于可重试范围,再循环发起请求。注意不是所有错误都该重试,比如 400(参数错误)或 401(鉴权失败)重试没意义。
- 只对连接失败(
CURLE_COULDNT_CONNECT)、超时(CURLE_OPERATION_TIMEDOUT)、网关类错误(502/503/504)重试 - 每次重试前用
usleep()延迟,避免雪崩,推荐从 100ms 开始指数退避(如 100 → 200 → 400) - 最多重试 3 次(含首次),超过就放弃,防止卡死
- 记得在每次循环中重新初始化
curl_init(),否则curl_setopt()可能复用旧状态
如何用 Guzzle 实现带退避的 AI 请求重试
Guzzle 7+ 内置了 RetryMiddleware,比手写 cURL 更可靠,尤其适合调用 OpenAI、Qwen、DeepSeek 等 RESTful AI API。
关键点在于配置 retry_strategy:它决定什么情况重试、重试几次、间隔多久。默认策略不重试 5xx,需自定义。
立即学习“PHP免费学习笔记(深入)”;
- 用
GuzzleHttp\Middleware::retry()构造中间件,传入判定函数:返回true才重试 - 判定函数里检查
$reason->getResponse()->getStatusCode()是否为502/503/504,或$reason->getException()是否是ConnectException/TimeoutException - 设置
delay为指数退避,例如function ($retries) { return (int) pow(2, $retries) * 100000; }(单位微秒) - 别忘了在
HandlerStack中注入中间件,否则不生效
$stack = HandlerStack::create(); $stack->push(Middleware::retry($decider, $delay)); $client = new Client(['handler' => $stack]);
重试时如何避免重复提交(幂等性)
AI 接口如 /v1/chat/completions 默认不保证幂等,连续发两次相同请求可能被服务端执行两次——尤其当第一次请求其实成功、但响应因网络丢包没收到时。
解决方法只有靠客户端加 Idempotency-Key 请求头,前提是目标 AI 服务支持(OpenAI 支持,部分私有部署模型不一定)。
- 生成唯一 key:用
bin2hex(random_bytes(16)),每次请求不同,但同一逻辑请求(比如重试)复用同一个 key - 把 key 存在变量里,不在循环内重新生成
- 注意:key 有效期通常为 24 小时,过期后重试会被当作新请求处理
- 如果服务端根本不支持该 header,那就只能接受「极低概率重复」,无法彻底规避
重试日志和监控不能少
线上 AI 请求重试率突然升高,往往是上游服务异常或本地网络配置出问题的早期信号。不记录,就等于瞎跑。
至少记录:原始请求参数(脱敏)、HTTP 方法、URL、触发重试的错误类型、重试次数、最终状态码、耗时。别只记“失败”,要记“为什么失败”。
- 用
error_log()或 PSR-3 logger 记录,级别设为warning(重试 1 次)或error(重试 3 次仍失败) - 避免记录完整 prompt,尤其是含用户数据的,可用
md5($prompt)或截取前 50 字符代替 - 在监控系统中聚合
ai_request_retry_count指标,阈值超过 5% 就告警 - 特别注意:重试逻辑本身不能引发新异常(比如日志写满磁盘),否则会掩盖真实问题
重试不是万能胶,它掩盖不了 DNS 配置错误、代理中断、SSL 证书过期这些底层问题。看到重试率上升,第一反应不该是调大重试次数,而是查网络链路和上游健康状态。



















