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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

吹落的树叶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值