curl_exec()返回false时应先通过curl_errno()判断错误类型:CURLE_OPERATION_TIMEDOUT等底层网络错误可重试,curl_errno为0但无HTTP响应或5xx状态码也可重试,4xx及业务错误不可重试;需配合curl_error()记录日志,合理设置超时时间(CONNECTTIMEOUT 3~5秒、TIMEOUT 10~15秒),采用带随机偏移的指数退避(如1/2/4秒)且最多重试3次;并发场景下失败请求须移出multi句柄后用独立cURL重试。

curl_exec() 返回 false 时怎么判断是否该重试
PHP 用 curl_exec() 发 POST 请求失败,不一定都要重试。得先区分是网络抖动(比如超时、连接拒绝),还是业务错误(比如接口返回 {"code":400,"msg":"参数错误"})。前者可重试,后者重试一百次也没用。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 检查
curl_errno($ch):值为CURLE_OPERATION_TIMEDOUT(28)、CURLE_COULDNT_CONNECT(7)、CURLE_SEND_ERROR(55)等属于可重试的底层错误 - 若
curl_errno()为 0,但curl_exec()返回false,要结合curl_getinfo($ch, CURLINFO_HTTP_CODE)看状态码——0表示根本没收到响应(大概率网络问题),5xx可考虑重试,4xx基本不重试 - 别只依赖返回值真假,一定要配合
curl_error($ch)打日志,否则重试五次失败,你连哪次卡在哪都不知道
设置合理的 CURLOPT_TIMEOUT 和 CURLOPT_CONNECTTIMEOUT
超时时间设太短,小概率丢包就直接失败;设太长,重试链路会卡住整个流程。尤其在高并发场景下,一个请求卡死 30 秒,可能拖垮整批任务。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
-
CURLOPT_CONNECTTIMEOUT控制建连阶段,建议设为3~5秒。DNS 解析慢、目标机器 SYN 队列满都发生在这阶段,没必要等太久 -
CURLOPT_TIMEOUT控制整个请求周期(含传输),建议设为10~15秒。如果后端本身处理就要 8 秒,那这个值至少得留出缓冲 - 千万别把两个 timeout 都设成 30 秒再搞 3 次重试——实际最长可能卡 90 秒,调用方早超时了
实现指数退避(exponential backoff)而不是固定间隔重试
连续请求失败时,立刻重试往往雪上加霜——对方服务可能正被压垮,你还在疯狂打它。固定等 1 秒重试,和等 1 秒、2 秒、4 秒,对下游压力完全不同。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 第 1 次失败后等待
1秒,第 2 次失败后等待2秒,第 3 次失败后等待4秒(即pow(2, $retryCount - 1)) - 加个随机偏移(jitter),比如
sleep(rand(0, 1000) / 1000 + $backoff),避免所有客户端在同一时刻重试造成脉冲流量 - 最大重试次数建议控制在
3次以内。超过 3 次还失败,大概率是配置错、地址错、证书过期或对方服务不可用,人工介入比继续重试更有效
用 curl_multi_exec() 并发请求时重试要单独隔离
很多人用 curl_multi_exec() 批量发请求,发现某个子请求失败了,想单独重试它——但直接在 multi 句柄里塞新 handle 会破坏状态机,容易导致句柄泄漏或 curl_multi_exec() 返回 CURLM_CALL_MULTI_PERFORM 死循环。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 不要在 multi 流程中动态增删 handle。失败的请求,应从 multi 句柄中
curl_multi_remove_handle()掉,再用独立的单 cURL 句柄重试 - 重试后的结果,不要强行塞回原 multi 的回调逻辑里,而是单独处理响应、记录日志、更新状态
- 如果必须批量重试,建议拆成两轮:第一轮用 multi 发,收集失败 ID;第二轮用单 cURL + 指数退避逐个重试,逻辑更可控
重试不是兜底银弹。真正难的不是写 for 循环,而是分清哪些错误能靠重试解决,哪些错误一出现就该报警、降级或跳过。尤其是跨公网调用第三方 API 时,对方的限流策略、证书更新、灰度发布,都会让“失败”变得五花八门。











