PHP 通过原生CURL实现非阻塞(并发)请求模式

前情提要

在实际开发中,往往有一段程序需要同时执行多个操作的场景,如果在此类场景中请求方式不当,到后面的逻辑堆积数据增多之后就会影响到程序响应效率,一般这种场景下就可以使用非阻塞模式,这里也可以理解为并发模式。

什么是阻塞和非阻塞

阻塞:是指应用程序执行IO操作需要彻底完成后才返回到用户空间

非阻塞:是指应用程序执行IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。

如何实现非阻塞模式

PHP实现非阻塞模式的方式有很多,比如在PHP-FPM模式下使用函数fastcgi_finish_request() 或者fsockopen()等方式来实现,这里将采用原生CURL的方式实现请求的非阻塞模式。

实现方式

主要实现核心其实是利用CURL中的 curl_multi_* 函数发送异步请求

  • 创建类文件 MultiHttpRequest.php,代码实现如下
<?php

class MultiHttpRequest
{
    public $requests = [];

    /**
     * 设置请求url
     *
     * @param $requests
     * @return $this
     */
    public function setRequests($requests) {
        $this->requests = $requests;
        return $this;
    }

    /**
     * 发送请求
     *
     * @return array|false
     */
    public function request()
    {
        if(!is_array($this->requests) or count($this->requests) == 0){
            return false;
        }

        $curl   = $response = [];
        $handle = curl_multi_init();
        foreach($this->requests as $k => $v){
            $url      = isset($v['url']) ? $v['url'] : '';
            $postData = isset($v['postData']) ? $v['postData'] : [];
            $header   = isset($v['header']) ? $v['header'] : [];
            $timeOut  = isset($v['timeOut']) ? $v['timeOut'] : 1;
            $proxy    = isset($v['proxy']) ? $v['proxy'] : '';
            $curl[$k] = $this->buildCurlObject($url, $postData, $header, $timeOut, $proxy);

            curl_multi_add_handle($handle, $curl[$k]);
        }

        $this->execHandle($handle);
        foreach ($this->requests as $key => $val){
            $response[$key] =  curl_multi_getcontent($curl[$key]);

            curl_multi_remove_handle($handle, $curl[$key]);
            curl_close($curl[$key]);
        }

        curl_multi_close($handle);

        return $response;
    }

    /**
     * 构造请求
     *
     * @param $url
     * @param $postData
     * @param $header
     * @param $timeOut
     * @param $proxy
     * @return false|resource
     */
    private function buildCurlObject($url, $postData, $header, $timeOut, $proxy) {

        $curl = curl_init();

        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_TIMEOUT, (int)$timeOut);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
        curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);

        // 配置代理
        if (!empty($proxy))
            curl_setopt($curl, CURLOPT_PROXY, $proxy);

        // 合并请求头部信息
        if(!empty($header))
            curl_setopt($curl, CURLOPT_HTTPHEADER, $header);

        // 是否是post请求
        if(!empty($postData) && is_array($postData)){
            curl_setopt($curl, CURLOPT_POST, true);
            curl_setopt($curl, CURLOPT_POSTFIELDS,  http_build_query($postData));
        }

        // 是否是https
        if(stripos($url,'https') === 0){
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        }

        return $curl;
    }

    /**
     * 执行批处理句柄
     *
     * @param $handle
     * @return void
     */
    private function execHandle($handle)
    {
        $active = true;
        $mrc = CURLM_OK;
        while ($active && $mrc == CURLM_OK) {
            do {
                $mrc = curl_multi_exec($handle, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);

            if (curl_multi_select($handle) == -1) {
                usleep(100);
            }
        }
    }
}
  • 调用程序,非阻塞请求
<?php

$response = new MultiHttpRequest();

$url = [
	['url' => 'https://www.baidu.com',
	'postData' =>['aaa'=>1],
	'header' => [
		"Content-Type:application/Json",
		"X-Requested-With:XMLhttpRequest"
	],
	'timeOut' => 2,
	'proxy' => '127.0.0.1'
	],
	['url' => 'https://www.baidu.com'],
	['url' => 'https://www.baidu.com'],
];

$response = $this->setRequests($url)->request();

var_dump($response);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Hello!请放心,我不会透露任何关于您的信息。 关于您的问题,可以使用PHPcurl_multi_init函数以及相关函数来实现并发请求。具体步骤如下: 1. 初始化一个curl多个句柄:curl_multi_init。 2. 循环添加每个要请求curl句柄:curl_multi_add_handle。 3. 执行curl句柄并等待请求完成:curl_multi_exec。 4. 使用curl_multi_select等待io事件准备就绪。 5. 获取请求结果:curl_multi_getcontent。 下面是一个简单的示例代码: ``` $urls = array( 'http://www.example.com/api/endpoint1', 'http://www.example.com/api/endpoint2', 'http://www.example.com/api/endpoint3', ); $handles = array(); $results = array(); // 初始化curl多个句柄 $multi_handle = curl_multi_init(); // 循环添加每个要请求curl句柄 foreach ($urls as $url) { $handle = curl_init($url); curl_setopt_array($handle, array( CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_SSL_VERIFYPEER => false, )); curl_multi_add_handle($multi_handle, $handle); $handles[] = $handle; } // 执行curl句柄并等待请求完成 do { $status = curl_multi_exec($multi_handle, $running); } while ($status === CURLM_CALL_MULTI_PERFORM || $running); // 使用curl_multi_select等待io事件准备就绪 if ($status !== CURLM_OK) { die("Error: " . curl_multi_strerror($status)); } do { $status = curl_multi_select($multi_handle, 1); if ($status === -1) { usleep(10); } // 获取请求结果 while ($info = curl_multi_info_read($multi_handle)) { $handle = $info['handle']; $content = curl_multi_getcontent($handle); $results[] = $content; curl_multi_remove_handle($multi_handle, $handle); curl_close($handle); } } while ($status === -1 || !empty($handles)); curl_multi_close($multi_handle); // 输出结果 print_r($results); ``` 希望这个回答对您有帮助!如果您有其它问题,请随时提出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吹落的树叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值