当前位置:首页 > 文章列表 > 文章 > php教程 > PHP curl处理 curl_multi_select 返回零的忙等的实现方法

PHP curl处理 curl_multi_select 返回零的忙等的实现方法

来源:17golang原创 2026-09-20 03:19:23 0浏览 收藏

在 PHP 的多请求循环里,curl_multi_select() 返回 0 不代表请求失败,它只说明这一次等待没有发现活动描述符。真正容易出问题的是马上再次调用 curl_multi_exec(),循环会变成无间隔的忙等。处理方式是:继续保留运行中的句柄,遇到 0 时短暂让出 CPU,再回到执行与完成消息读取;只有返回 -1 才按 select 失败处理。

官方地址:https://www.php.net/

要点速览
  • curl_multi_select() 返回正数表示有描述符活动,返回 0 表示本轮没有活动,返回 -1 表示底层等待失败。
  • 返回 0 时用一个很短的 usleep() 让出 CPU,然后继续调用 curl_multi_exec(),不要把 0 当成完成。
  • 请求完成后通过 curl_multi_info_read() 读取单个结果,移除句柄并关闭资源;multi 栈返回 OK 也不等于每个请求都成功。

先把 multi 请求循环跑起来

multi API 的核心不是一次性执行所有 URL,而是反复推进一组句柄。$running 表示仍在处理的句柄数量;只要它大于 0,就继续执行。下面的示例使用两个公共地址作为演示目标,实际项目应替换成自己的接口,并为每个句柄保存业务标识。

 true,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_TIMEOUT => 15,
    ]);
    curl_multi_add_handle($multi, $handle);
    $handles[(int) $handle] = $handle;
}

$running = 0;
do {
    // 推进所有可继续读写的句柄;返回值只表示 multi 栈层面的状态。
    $multiCode = curl_multi_exec($multi, $running);
    if ($multiCode !== CURLM_OK) {
        throw new RuntimeException(curl_multi_strerror($multiCode));
    }

    // 先处理本轮已经完成的请求,再决定是否等待下一次网络活动。
    while (($info = curl_multi_info_read($multi)) !== false) {
        $done = $info['handle'];
        $status = $info['result'];
        if ($status !== CURLE_OK) {
            error_log(curl_error($done));
        }
        curl_multi_remove_handle($multi, $done);
        curl_close($done);
    }

    if ($running > 0) {
        // 只等待仍在运行的请求;返回值分支见下一节。
        $selected = curl_multi_select($multi, 1.0);
        if ($selected === 0) {
            // 没有活动描述符时让出 1 毫秒,避免 while 循环占满 CPU。
            usleep(1000);
        } elseif ($selected === -1) {
            // -1 是等待失败,不应与“本轮无活动”的 0 混为一谈。
            usleep(1000);
        }
    }
} while ($running > 0);

curl_multi_close($multi);
?>

这段代码把“推进传输”“读取完成消息”和“等待下一次活动”分开,后续排查时可以分别计数。若项目需要保留响应正文,应在完成分支调用 curl_multi_getcontent($done) 后再释放句柄。

curl_multi_select 返回值要按三种状态处理

官方手册把返回值定义得很清楚:成功时返回活动描述符数量,可能为 0;底层 select() 失败时返回 -1。因此,0 的正确语义是“现在没有可读写事件”,不是“所有请求已完成”,也不是“发生错误”。

返回值含义循环动作
大于 0发现活动描述符立即回到 curl_multi_exec()
等于 0本轮没有活动短暂 usleep() 后重试
等于 -1等待调用失败记录上下文,按项目策略重试或终止
PHP curl_multi_exec 与 curl_multi_select 围绕返回 0、正数和负一的事件轮询结构说明图
图1:PHP cURL 多句柄轮询的返回值分支说明图,不是运行截图。

忙等通常表现为 CPU 占用突然升高、循环次数远超请求数,而网络吞吐没有变化。不要用“把 select 超时设得极小”来掩盖问题;超时负责等待上限,返回 0 后的让出间隔负责避免空转,两个参数解决的是不同层次的问题。

返回零时怎样让出 CPU 又不拖慢请求

返回 0 后的处理没有一个适用于所有机器和网络的固定数字。可以从 usleep(1000) 开始,也就是让出约 1 毫秒,再继续执行;高并发服务应结合循环次数、单批请求耗时和进程 CPU 使用率调整。间隔过短,忙等仍可能存在;间隔过长,响应尾延迟会增加。

还有两个边界需要记住:

  • $running 仍大于 0 时才等待。它变成 0 后,先把完成消息读完,再结束循环。
  • 不要因为 curl_multi_exec() 返回 CURLM_OK 就判定所有 HTTP 请求成功;单个句柄的 $info['result']、HTTP 状态码和业务响应仍要单独检查。

如果需要统一封装,可以只把“等待”写成一个小函数,让业务代码继续关注结果:

读取完成消息并回收句柄

curl_multi_info_read() 读取的是已经完成的传输消息。完成后先取出句柄和传输结果,再读取正文或 HTTP 信息,随后调用 curl_multi_remove_handle()curl_close()。这样,事件等待层不会把已完成句柄继续留在轮询集合里。

PHP CURLMSG_DONE 完成消息经过 result、curl_multi_getcontent 到移除和关闭句柄的关系结构图
图2:完成消息到句柄回收的关系结构图,不是运行截图。

排查时可记录四个指标:调用 curl_multi_exec() 的次数、curl_multi_select() 返回 0 的次数、返回 -1 的次数,以及每个请求的最终 result。如果 0 次数很多但 CPU 已恢复,说明让出策略生效;如果 -1 持续出现,则应先查运行环境与 libcurl 行为,而不是继续加大循环频率。

相关问题

curl_multi_select 返回 0 是错误吗?

不是。它表示等待窗口内没有活动描述符,仍需回到 curl_multi_exec() 判断传输是否推进或完成。

返回 0 后可以直接 continue 吗?

可以继续轮询,但不建议无间隔 continue。短暂 usleep() 能给出让出 CPU 的机会,具体间隔应通过指标调整。

如何判断某一个请求真的失败?

读取 curl_multi_info_read() 返回的 result,并结合 curl_getinfo() 的 HTTP 状态码和业务响应判断;不能只看 multi 层返回值。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
LibTV能做AI视频创作吗?功能范围和适用场景LibTV能做AI视频创作吗?功能范围和适用场景
上一篇
LibTV能做AI视频创作吗?功能范围和适用场景
Go maps 包复制 map 并隔离写入的实现方式
下一篇
Go maps 包复制 map 并隔离写入的实现方式
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    122次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    196次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    140次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    114次使用
  • CMMLU中文大模型评估基准:功能、使用教程与应用场景解析
    CMMLU
    深入了解CMMLU中文评估基准,涵盖67个学科主题,提供数据集下载、Zero-shot/Five-shot评估方法及排行榜,助力优化中文语言模型性能。
    101次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码