PHP curl并发请求

前言

使用PHP的curl函数调用多个外部HTTP接口时,默认情况下是需要一个一个执行的,第一个请求结束后才会发起第二个请求,这样会显得效率低。如果各个请求之间没有强依赖关系,可以使用curl的multi系列函数来同时发起多个请求,这样可以节省很多时间,提高效率。

有很多资料说curl_multi是使用多线程来实现并发的,但事实上并不是,它仍然是单线程,只是用了系统的select I/O多路复用机制来实现并发。

测试环境

PHP 7.3

代码

<?php

function concurrency_request(array $requests): array
{
    $chHandles = [];

    /* 根据请求信息创建ch句柄 */
    foreach ($requests as $k => $req) {
        $options = [
            CURLOPT_HEADER         => false,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 30,
        ];
        if (!empty($req['header'])) {
            $options[CURLOPT_HTTPHEADER] = $req['header'];
        }

        switch ($req['method']) {
            case 'GET':
                $options[CURLOPT_URL] = "{$req['url']}?" . http_build_query($req['param']);
                break;
            case 'POST':
                $options[CURLOPT_URL] = $req['url'];
                $options[CURLOPT_POST] = true;
                $options[CURLOPT_POSTFIELDS] = http_build_query($req['param']);
                break;
            default:
                throw new Exception("不支持的HTTP方法:{$req['method']}");
        }

        $ch = curl_init();
        curl_setopt_array($ch, $options);
        $chHandles[$k] = $ch;
    }

    $mh = curl_multi_init();
    foreach ($chHandles as $ch) {
        curl_multi_add_handle($mh, $ch);
    }

    /* 不断执行,直至所有请求完成 */
    $running = null;
    do {
        $errCode = curl_multi_exec($mh, $running);
        if ($errCode !== CURLM_OK) {
            throw new Exception("curl_multi_exec执行失败: " . curl_multi_strerror($errCode));
        }
        curl_multi_select($mh); // 阻塞直至有数据可读,默认阻塞最多一秒,阻塞期间不占用CPU资源
    } while ($running > 0);

    /* 读取请求结果 */
    while ($info = curl_multi_info_read($mh)) {
        $ch = $info['handle'];
        if ($info['result'] !== 0) {
            $httpCode = $info['result']; // $info['result']的值等同于curl_errno($ch)
            $httpBody = curl_error($ch);
        } else {
            $httpCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
            $httpBody = curl_multi_getcontent($ch);
        }
        $index = array_search($ch, $chHandles, true);
        $requests[$index]['response'] = [
            'code' => $httpCode, // HTTP状态码或cURL错误码
            'body' => $httpBody, // HTTP body或cURL错误信息
        ];

        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }

    curl_multi_close($mh);

    return $requests;
}

function main()
{
    /* 要请求的接口 */
    $requests = [
        [
            'url'    => 'https://www.baidu.com', // 请求URL
            'method' => 'GET', // 请求方法,支持GET和POST
            'header' => [], // 请求头
            'param'  => ['i' => 0], // 请求参数
        ],
        [
            'url'    => 'http://www.ssssdhaidhoasdhaihd.com',
            'method' => 'GET',
            'header' => [],
            'param'  => ['i' => 1],
        ],
    ];
    $results = concurrency_request($requests);
    echo "请求结果: " . json_encode($results) . "\n";
}

main();

注意的地方

不要尝试从$chHandles里的原始句柄处获取错误代码和错误信息,例如:

foreach ($chHandles as $ch) {
    $errCode = curl_errno($ch); // 获取到的值永远为0
    $errMsg = curl_error($ch); // 获取到的值永远为空字符串
}

这是因为curl_multi在遇到请求错误时,不会把错误信息写入到原始句柄处,正确的做法是使用curl_multi_info_read函数来获取句柄:

while ($info = curl_multi_info_read($mh)) {
    $ch = $info['handle'];
    $errCode = curl_errno($ch);
    $errMsg = curl_error($ch);
    if ($errCode !== 0) {
        exit("出错啦");
    }
}
智能网联汽车的安全员高级考试涉及多个方面的专业知识,包括但不限于自动驾驶技术原理、车辆传感器融合、网络安全防护以及法律法规等内容。以下是针对该主题的一些核心知识解析: ### 关于智能网联车安全员高级考试的核心内容 #### 1. 自动驾驶分级标准 国际自动机工程师学会(SAE International)定义了六个级别的自动驾驶等级,从L0到L5[^1]。其中,L3及以上级别需要安全员具备更高的应急处理能力。 #### 2. 车辆感知系统的组成与功能 智能网联车通常配备多种传感器,如激光雷达、毫米波雷达、摄像头和超声波传感器等。这些设备协同工作以实现环境感知、障碍物检测等功能[^2]。 #### 3. 数据通信与网络安全 智能网联车依赖V2X(Vehicle-to-Everything)技术进行数据交换,在此过程中需防范潜在的网络攻击风险,例如中间人攻击或恶意软件入侵[^3]。 #### 4. 法律法规要求 不同国家和地区对于无人驾驶测试及运营有着严格的规定,考生应熟悉当地交通法典中有关自动化驾驶部分的具体条款[^4]。 ```python # 示例代码:模拟简单决策逻辑 def decide_action(sensor_data): if sensor_data['obstacle'] and not sensor_data['emergency']: return 'slow_down' elif sensor_data['pedestrian_crossing']: return 'stop_and_yield' else: return 'continue_driving' example_input = {'obstacle': True, 'emergency': False, 'pedestrian_crossing': False} action = decide_action(example_input) print(f"Action to take: {action}") ``` 需要注意的是,“同学”作为特定平台上的学习资源名称,并不提供官方认证的标准答案集;建议通过正规渠道获取教材并参加培训课程来准备此类资格认证考试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值