PHP curl_multi_info_read函数(保姆级教程)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

在 PHP 开发中,网络请求是常见的操作场景,无论是调用第三方 API 还是抓取网页数据,都离不开 cURL 库的支持。然而,当需要同时发起多个 HTTP 请求时,传统的单线程方式效率低下,容易导致程序阻塞。此时,PHP curl_multi_info_read 函数便成为优化并发请求的关键工具之一。本文将从基础概念、函数原理、代码示例到实战案例,系统讲解如何通过 curl_multi_info_read 实现高效多线程请求,帮助开发者提升代码性能与用户体验。


一、理解多线程请求与 curl_multi 系列函数

1.1 什么是多线程请求?

多线程请求是指在同一个 PHP 脚本中同时发起多个 HTTP 请求,而非逐个等待每个请求完成。这类似于快递公司同时派送多个包裹,而非按顺序逐个送货。通过这种方式,可以显著缩短总耗时,尤其在需要同时获取多个外部资源时效果显著。

1.2 curl_multi 系列函数的核心作用

PHP 的 curl_multi 系列函数提供了实现多线程请求的能力,其中关键函数包括:

  • curl_multi_init():初始化多线程句柄。
  • curl_multi_add_handle():向句柄中添加单个请求的 cURL 句柄。
  • curl_multi_exec():执行所有添加的请求,并返回当前执行状态。
  • curl_multi_info_read():获取已完成请求的详细信息。

这四个函数协同工作,共同构建了 PHP 多线程请求的完整流程。


二、curl_multi_info_read 函数详解

2.1 函数基础语法

array|bool curl_multi_info_read ( resource $multi_handle , int &$msgs_in_queue )  
  • 参数说明
    • multi_handle:通过 curl_multi_init() 初始化的多线程句柄。
    • msgs_in_queue:返回队列中剩余未读取的完成请求数量。
  • 返回值
    成功时返回关联数组(包含请求结果),失败时返回 false

2.2 函数工作原理

curl_multi_info_read 的核心作用是从多线程队列中提取已完成请求的信息。它与 curl_multi_exec 配合使用,形成一个“执行-查询”的循环:

  1. curl_multi_exec 持续执行所有未完成的请求;
  2. 当某个请求完成时,它会进入“已完成队列”;
  3. 开发者通过 curl_multi_info_read 逐个读取该队列中的结果。

比喻解释
想象一个快递分拣站:

  • curl_multi_exec 负责将包裹(请求)分发到不同快递员手中;
  • 完成的包裹会被放入“已送达”货架;
  • curl_multi_info_read 就是仓库管理员逐个取走货架上的包裹并记录信息。

三、函数返回值的解析

3.1 返回数组的常见字段

调用 curl_multi_info_read 后,返回的数组包含以下关键信息:
| 字段名 | 描述 |
|-----------------|----------------------------------------------------------------------|
| msg | 始终为 CURLMSG_DONE,表示请求已完成。 |
| result | 请求的最终状态码(如 CURLE_OK 表示成功)。 |
| handle | 完成请求的 cURL 句柄资源,用于后续操作(如 curl_close)。 |
| http_code | HTTP 响应状态码(如 200404)。 |
| total_time | 请求的总耗时(单位:秒,浮点数)。 |

3.2 示例返回值分析

Array  
(  
    [msg] => 1  
    [result] => 0  
    [handle] => Resource id #5  
    [http_code] => 200  
    [total_time] => 0.234  
)  
  • msg=1 表示 CURLMSG_DONE
  • result=0 表示 CURLE_OK(无错误);
  • http_code=200 说明请求成功;
  • total_time 帮助开发者优化性能瓶颈。

四、代码示例:实现多线程请求

4.1 基础用法示例

// 1. 初始化多线程句柄  
$mh = curl_multi_init();  

// 2. 创建两个 cURL 句柄并添加到多线程  
$urls = [  
    'https://api.example.com/data1',  
    'https://api.example.com/data2'  
];  

foreach ($urls as $url) {  
    $ch = curl_init($url);  
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
    curl_multi_add_handle($mh, $ch);  
}  

// 3. 执行请求并循环检查结果  
do {  
    $status = curl_multi_exec($mh, $active);  
} while ($status === CURLM_CALL_MULTI_PERFORM || $active > 0);  

// 4. 读取已完成的请求信息  
while ($info = curl_multi_info_read($mh)) {  
    if ($info['msg'] === CURLMSG_DONE) {  
        $handle = $info['handle'];  
        $response = curl_multi_getcontent($handle);  
        echo "请求状态码:" . $info['http_code'] . "\n";  
        echo "响应内容:" . $response . "\n";  
        curl_close($handle); // 关闭句柄  
    }  
}  

// 5. 关闭多线程句柄  
curl_multi_close($mh);  

4.2 代码流程解析

  1. 初始化与添加句柄:通过 curl_multi_init 创建多线程容器,并逐个添加单个请求的句柄。
  2. 执行与循环检查curl_multi_exec 持续执行请求,直到所有请求完成或无活动句柄。
  3. 读取结果:通过循环调用 curl_multi_info_read,逐个处理完成的请求数据。
  4. 资源释放:关闭所有 cURL 句柄和多线程句柄,避免内存泄漏。

五、实战案例:批量获取 API 数据

5.1 场景描述

假设需要同时获取三个天气 API 的数据,但每个 API 的响应时间存在差异。通过 curl_multi_info_read 实现并发请求,最终将结果整合输出。

5.2 完整代码实现

<?php  
$mh = curl_multi_init();  
$urls = [  
    'https://api.weather1.com/forecast',  
    'https://api.weather2.com/forecast',  
    'https://api.weather3.com/forecast'  
];  

// 创建并添加所有请求  
foreach ($urls as $key => $url) {  
    $ch[$key] = curl_init($url);  
    curl_setopt_array($ch[$key], [  
        CURLOPT_RETURNTRANSFER => true,  
        CURLOPT_TIMEOUT => 5, // 设置超时时间为5秒  
    ]);  
    curl_multi_add_handle($mh, $ch[$key]);  
}  

// 执行请求  
do {  
    $status = curl_multi_exec($mh, $active);  
} while ($status === CURLM_CALL_MULTI_PERFORM || $active > 0);  

// 处理完成的请求  
$completed = [];  
while ($info = curl_multi_info_read($mh)) {  
    if ($info['msg'] === CURLMSG_DONE) {  
        $handle = $info['handle'];  
        $response = curl_multi_getcontent($handle);  
        $httpCode = $info['http_code'];  
        $completed[] = [  
            'url' => curl_getinfo($handle, CURLINFO_EFFECTIVE_URL),  
            'status' => $httpCode,  
            'data' => json_decode($response, true) ?? '请求失败',  
            'time' => $info['total_time']  
        ];  
        curl_close($handle);  
    }  
}  

// 输出结果  
echo "所有请求已完成,总耗时:" . array_sum(array_column($completed, 'time')) . "秒\n";  
foreach ($completed as $item) {  
    echo "URL: " . $item['url'] . "\n";  
    echo "状态码:" . $item['status'] . "\n";  
    echo "数据:" . json_encode($item['data'], JSON_UNESCAPED_UNICODE) . "\n\n";  
}  

curl_multi_close($mh);  

5.3 关键点解析

  • 超时控制:通过 CURLOPT_TIMEOUT 防止单个请求阻塞整体流程。
  • 错误处理:使用 json_decode 的默认值(?? '请求失败')捕获解析异常。
  • 性能统计:通过 total_time 计算总耗时,帮助优化 API 调用策略。

六、常见问题与解决方案

6.1 问题1:curl_multi_info_read 返回 false 是为什么?

可能原因

  • 队列中已无待读取的完成请求。
  • 多线程句柄($mh)未正确初始化或已关闭。

解决方案

  • 确保在 curl_multi_exec 完成后再调用 curl_multi_info_read
  • 在循环中检查返回值,避免重复读取。
while ($info = curl_multi_info_read($mh)) {  
    // 处理逻辑  
}  

6.2 问题2:如何处理超时或失败的请求?

策略

  • curl_setopt 中设置 CURLOPT_CONNECTTIMEOUTCURLOPT_TIMEOUT
  • 根据返回的 $info['http_code']$info['result'] 进行条件判断。
if ($info['result'] !== CURLE_OK) {  
    echo "请求失败,错误代码:" . $info['result'] . "\n";  
    // 记录日志或重试逻辑  
}  

结论

通过本文的讲解,读者应已掌握 PHP curl_multi_info_read 函数的核心原理、用法及实际应用场景。该函数是构建高效 PHP 多线程请求的关键工具,尤其在需要批量获取外部资源时,能够显著提升程序性能。未来,随着项目需求的复杂化,开发者可进一步结合协程或异步框架(如 Swoole)实现更高级的并发管理。建议读者通过实际案例练习,逐步掌握多线程编程的最佳实践,为构建高并发系统打下坚实基础。

最新发布