使用php curl 的并发能力可以做什么

在php中,没有多线程让编程变得简单。但在一些需要并发提升性能的场景下,显得有些无能为力,比如发起一些http请求。

但好在curl扩展可以让我们“并发”去请求网络资源。利用这个特点,我们能做很多有趣的事情。

最基础的,并发请求网络资源,提升处理速度。

并发访问代码

<?php
class ConcurrencyHTTP {
    private $_requests;
    private $_callbacks;
    private $_currentIndex = 0;
    public function get($url, $header = array(), $timeout = 30) {
    $this->_requests[$this->_currentIndex++] = array(
            "url"       => $url,
            "method"    => "GET",
            "timeout"   => $timeout,
            "header"    => $header
        );
        return $this;
    }

    public function post($url, $body, $header = array(), $timeout = 30)
    {
        $this->_requests[$this->_currentIndex++] = array(
            "url"       => $url,
            "method"    => "GET",
            "body"      => $body,
            "timeout"   => $timeout,
            "header"    => $header
        );
        return $this;
    }

    public function callback(callable $callback)
    {
        $currentIndex = $this->_currentIndex - 1;
        $this->_callbacks[$currentIndex] = $callback;
        return $this;
    }

    public function exec()
    {
        foreach ($this->_requests as $req) {
            $options = array(
                CURLOPT_HEADER          => $req["header"],
                CURLOPT_RETURNTRANSFER  => true,
                CURLOPT_TIMEOUT         => $req["timeout"],
                CURLOPT_URL             => $req["url"],
            );
            if ($req["method"] == "POST") {
                $options[CURLOPT_POST] = true;
                if (is_array($req["body"])) {
                    $options[CURLOPT_POSTFIELDS] = http_build_query($req['body']);
                } else {
                    $options[CURLOPT_POSTFIELDS] = $req['body'];
                }
            }
            $ch = curl_init();
            curl_setopt_array($ch, $options);
            $handles[] = $ch;
        }

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

        $running = null;
        $finished = 0;
        do {
            curl_multi_exec($mh, $running);
            curl_multi_select($mh);

            while ($info = curl_multi_info_read($mh)) {
                $ch = $info['handle'];
                if ($info['result'] != CURLM_OK) {
                    $code = $info['result'];
                    $data = curl_error($ch);
                } else {
                    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                    $data = curl_multi_getcontent($ch);
                }
                $index = array_search($ch, $handles, true);
                $this->_callbacks[$index]($data, $code);
                curl_multi_remove_handle($mh, $ch);
                curl_close($ch);
                $finished++;
            }
        } while ($running > 0);
        curl_multi_close($mh);
    }
}

调用很简单,直接链式调用get和callback即可。get用于组装请求数据,callback用户设置数据到来时的回调函数。

$concurrency = new ConcurrencyHTTP();
$concurrency->get("http://baidu.com")
->callback(function($content, $code) {
echo $content;
echo $code;
})
->get("http://imhuchao.com")
->callback(function($content, $code) {
echo $content;
echo $code;
})
->exec();

在某些情况下,如果同一个服务我们有多个节点,我们可能需要取回响应速度比较快的节点。

获取最快的节点

上面的代码稍微改改就能实现,我们改动exec方法,增加一个fastestOne参数。当有数据返回时立即结束。改动结果如下:

public function exec($fastestOne = false)
    {
        foreach ($this->_requests as $req) {
            $options = array(
                CURLOPT_HEADER          => $req["header"],
                CURLOPT_RETURNTRANSFER  => true,
                CURLOPT_TIMEOUT         => $req["timeout"],
                CURLOPT_URL             => $req["url"],
            );
            if ($req["method"] == "POST") {
                $options[CURLOPT_POST] = true;
                if (is_array($req["body"])) {
                    $options[CURLOPT_POSTFIELDS] = http_build_query($req['body']);
                } else {
                    $options[CURLOPT_POSTFIELDS] = $req['body'];
                }
            }
            $ch = curl_init();
            curl_setopt_array($ch, $options);
            $handles[] = $ch;
        }

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

        $running = null;
        $finished = 0;
        do {
            curl_multi_exec($mh, $running);
            curl_multi_select($mh);

            while ($info = curl_multi_info_read($mh)) {
                $ch = $info['handle'];
                if ($info['result'] != CURLM_OK) {
                    $httpCode = $info['result'];
                    $httpBody = curl_error($ch);
                } else {
                    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                    $httpBody = curl_multi_getcontent($ch);
                }
                $index = array_search($ch, $handles, true);
                $this->_callbacks[$index]($httpBody, $httpCode);
                curl_multi_remove_handle($mh, $ch);
                curl_close($ch);
                $finished++;
            }
            // 当有返回结果时,break跳出循环,不再等待其他结果
            if ($fastestOne && $finished > 0) {
                break;
            }
            
        } while ($running > 0);
        curl_multi_close($mh);
    }

 

你可能还喜欢下面这些文章

ajax的核心,好好认识一下XMLHttpRequest

相信包括在我的绝大多数人都用jQuery的$.get(),$.post(),$.ajax()方法用的很爽了,关于其原生的请求却很少去发掘,很多时候(比如用html5开发app的时候),我并不再需要jQuery,弄明白XMLHttpRequest用原生的就能很好的处理ajax了。首先,由于我的js是通过jQuery入门的,所以才会有这篇文章。从new一个对象开始var xmlhttp = new XMLHttpRequest();之后的请求,读取,出错等等各种处理都在xmlhttp这个对象里面啦第一个GET请求get请求简单,最适合入门操作啦。之前new了一个xmlhttp对象,这次我们就要对它

数据结构学习笔记:树

树树是一种层次关系,在日常生活非常常见,比如社会关系,亲缘关系,文件管理。一棵树是一些节点的集合,这个集合可以是空集,若非空,则一棵树由称作为根的节点r以及0个或者多个非空的(子)树组成,这些子树中的每一棵都是被来自根r的一条有向的边所连接。一种数据结构需要包含一些操作,树这种数据结构有增加,删除,查找,修改。节点节点的度:节点子树的个数。叶子节点:没有儿子的节点,也就是度为0的节点。节点的层次:规定跟节点在1层,其他节点的层次为父节点的层次加1。节点的高:节点的高为从这个节点到叶子的最长路径,所有树叶的高都是0。节点的深度:从跟节点到该节点的唯一路径长,根的深度为0。节点定义typedef

Go入门:五、goroutine和channel

这是我Go学习的第五篇笔记,学习的是go的语言的其他特性,这些特性是其他语言所不具备的。这次主要学习的是goroutine和channel。我的语言学习过程一般分为下面几个:1. 变量和数据类型2. 流程控制方法3. 函数声明和调用4. 面向对象5. 语言特性6. 常用标准库goroutine介绍和使用Go语言中,每个并发执行的单元称为goroutine(可类比线程)。当一个程序启动时候,main函数在一个main goroutine中运行。如果想要创建新的goroutine,使用go关键字!语法创建一个新的 goroutinechannel是goroutine的通信机制,比如创建一个能够接收

一致性哈希的php实现

未来项目可能要上memcache集群,memcache集群的key分配完全在客户端完成,服务端不做任何处理,这里对key进行分配节点的最优方式就是使用一致性哈希。记得以前用mysql进行分库分表的时候,通常会用一个求余作为哈希函数,这样一些id就能对应相应的表了。不过使用mysql的时候,我们不需要考虑这些节点失效问题,以及节点增加或者减少的问题(在此之前应该做好足够的计划和准备),但是对于缓存,通常就比较宽松了,允许节点失效问题,但是普通的hash分配在节点失效之后,大部分的缓存位置都改变了,这显然个灾难,这个时候就要考虑一致性hash了,在增加或者删除节点,只有小部分的key会受影响。一致

JS使用XMLHttpRequest实现ajax请求

是一个JavaScript对象,它最初由微软设计,随后被 Mozilla、Apple和Google采纳。如今,该对象已经被 W3C组织标准化。通过它,你可以很容易的取回一个URL上的资源数据。尽管名字里有XML, 但 可以取回所有类型的数据资源,并不局限于XML。而且除了HTTP ,它还支持 和 协议。创建一个 实例, 可以使用如下语句:方法概述非标准方法属性AttributeTypeDescription一个JavaScript函数对象,当readyState属性改变时会调用它。回调函数会在user interface线程中调用。警告: 不能在本地代码中使用. 也不应该在同步模式的请求中

并发任务分配问题

这是在工作中遇到的实际问题和解决过程。问题已经被抽象成并发任务的分配问题。问题如果有 n 组数据均分给 m 个处理器处理,那么每个处理器分到的数据是 。如果n组数据的类型有差异,其中有a组是一类数据,剩余 n-a 组是另一类数据。只有同类数据才能被一次性处理,那么该如何分配?这个问题在现实中是存在的。比如HTTP并发请求处理一些数据。数据被批量送来,但类型不一样。为了节省耗时,我们希望并发处理这些不同的数据。并发数是确定好的。现在需要计算每个请求处理的数量,以便我们能给每一个请求打包数据。求解n 组数据交给 m 个处理器处理,每个处理器最多分到 组数据,这是毫无疑问的。如果 n 组数据中有

CGI与FastCGI是什么

当我们在谈到cgi的时候,我们在讨论什么最早的Web服务器简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html。事物总是不 断发展,网站也越来越复杂,所以出现动态技术。但是服务器并不能直接运行 php,asp这样的文件,自己不能做,外包给别人吧,但是要与第三做个约定,我给你什么,然后你给我什么,就是握把请求参数发送给你,然后我接收你的处 理结果给客户端。那这个约定就是 common gateway interface,简称cgi。这个协议可以用vb,c,php,python 来实现。cgi只是接口协议,根本不是什么语言。下面图可以看到流程WEB服

php-fpm与fastcgi之间的关系

php-fpm与fastcgi名词解释php-fpm 全称为php fastcgi progress manager (php fastcgi 进程管理器)FastCGI全称为fast common gateway interface (Fast 通用网关接口)FastCGI是一种协议Fastcgi是CGI的升级版,一种语言无关的协议,用来沟通程序(如PHP, Python, Java)和Web服务器(Apache2, Nginx), 理论上任何语言编写的程序都可以通过Fastcgi来提供Web服务。Fastcgi的特点是会在一个进程中依次完成多个请求,以达到提高效率的目的,大多数Fastcg

php的file_get_contents()的高级用法

读取文件,读取网页,file_get_contents总是首选。既简单,又高效。读取网页: $content = file_get_contents("http://imhuchao.com")这里要说的是file_get_contents的一些"高级"的用法,平时大概用不上。file_get_contents可以用来发送post请求,设定超时时间等等,不弱于curl。函数说明是这样子的string file_get_contents ( string $filename ]]] )其中第三个参数$context能够让file_get_content发送post请求,控制超时等功能先看一个简单

awk分析nginx日志中的网页响应时间

nginx日志可以十分方便的看到每一个请求的响应速度,通常我会用awk去分析这些请求耗时。通常nginx的log配置是这样的log_format access_comment '$remote_addr - $remote_user "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" $http_x_forwarded_for ' '$upstream_response_time $request_time';我们记录的日志类似于这样127.0.0.1 - - "POST /get" "M

赞赏

微信赞赏支付宝赞赏

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注