使用PHP可以实现异步HTTP请求吗?

时间:2011-03-27 23:24:50

标签: php curl

我有一个需要从远程服务器下载多个文件的PHP脚本。目前我只是循环下载并使用cURL处理文件,这意味着它不会开始下载一个文件,直到前一个文件完成 - 这会显着增加脚本运行时间。

是否可以启动多个cURL实例,例如,同时异步下载这些文件而无需等待前一个文件完成?如果是这样,这将如何实现?

5 个答案:

答案 0 :(得分:21)

结帐curl-easy。它同时支持并行或单个请求的阻塞和非阻塞请求。此外,它是经过单元测试的,与许多简单或有缺陷的库不同。

披露:我是这个图书馆的作者。图书馆拥有自己的测试套件,所以我非常有信心它是健壮的。

另外,请查看以下使用示例:

<?php
// We will download info about 2 YouTube videos:
// http://youtu.be/XmSdTa9kaiQ and
// http://youtu.be/6dC-sm5SWiU

// Init queue of requests
$queue = new cURL\RequestsQueue;
// Set default options for all requests in queue
$queue->getDefaultOptions()
    ->set(CURLOPT_TIMEOUT, 5)
    ->set(CURLOPT_RETURNTRANSFER, true);
// Set callback function to be executed when request will be completed
$queue->addListener('complete', function (cURL\Event $event) {
    $response = $event->response;
    $json = $response->getContent(); // Returns content of response
    $feed = json_decode($json, true);
    echo $feed['entry']['title']['$t'] . "\n";
});

$request = new cURL\Request('http://gdata.youtube.com/feeds/api/videos/XmSdTa9kaiQ?v=2&alt=json');
$queue->attach($request);

$request = new cURL\Request('http://gdata.youtube.com/feeds/api/videos/6dC-sm5SWiU?v=2&alt=json');
$queue->attach($request);

// Execute queue
$queue->send();

答案 1 :(得分:17)

是。

multirequest PHP library(或参见:archived Google Code project)。它是一个多线程CURL库。

作为另一种解决方案,您可以编写一个脚本,以支持线程的语言(如Ruby或Python)执行此操作。然后,用PHP调用脚本。看起来很简单。

答案 2 :(得分:1)

@stil 的库是如此,很酷。非常感谢他!

尽管如此,我编写了一个很好的实用程序函数,可以很容易地从多个URL(在我的情况下是API)中获取异步内容并返回它们而不会丢失哪些信息。

您只需传递 key =&gt;即可运行它value 数组作为输入,它返回 key =&gt;响应数组结果: - )

/** 
     * This function runs multiple GET requests parallely.<br />
     * @param array $urlsArray needs to be in format:<br />
     * <i>array(<br />
     * [url_unique_id_1] => [url_for_request_1],<br />
     * [url_unique_id_2] => [url_for_request_2],<br />
     * [url_unique_id_3] => [url_for_request_3]<br />
     * )</i><br />
     * e.g. input like:<br />
     *  <i>array(<br />
     * &nbsp; "myemail@gmail.com" =>
     * &nbsp; "http://someapi.com/results?search=easylife",<br />
     * &nbsp; "michael@gmail.com" =>
     * &nbsp; "http://someapi.com/results?search=safelife"<br />
     * )</i>
     * @return array An array where for every <i>url_unique_id</i> response to this request 
     * is returned e.g.<br />
     * <i>array(<br />
     * &nbsp; "myemail@gmail.com" => <br />
     * &nbsp; "Work less, enjoy more",<br />
     * &nbsp; "michael@gmail.com" => <br />
     * &nbsp; "Study, work, pay taxes"<br />
     * )</i>
     *  */
    public function getResponsesFromUrlsAsynchronously(array $urlsArray, $timeout = 8) {
        $queue = new \cURL\RequestsQueue;

        // Set default options for all requests in queue
        $queue->getDefaultOptions()
                ->set(CURLOPT_TIMEOUT, $timeout)
                ->set(CURLOPT_RETURNTRANSFER, true);

        // =========================================================================
        // Define some extra variables to be used in callback

        global $requestUidToUserUrlIdentifiers;
        $requestUidToUserUrlIdentifiers = array();

        global $userIdentifiersToResponses;
        $userIdentifiersToResponses = array();

        // =========================================================================

        // Set function to be executed when request will be completed
        $queue->addListener('complete', function (\cURL\Event $event) {

            // Define user identifier for this url
            global $requestUidToUserUrlIdentifiers;
            $requestId = $event->request->getUID();
            $userIdentifier = $requestUidToUserUrlIdentifiers[$requestId];

            // =========================================================================

            $response = $event->response;
            $json = $response->getContent(); // Returns content of response

            $apiResponseAsArray = json_decode($json, true);
            $apiResponseAsArray = $apiResponseAsArray['jobs'];

            // =========================================================================
            // Store this response in proper structure
            global $userIdentifiersToResponses;
            $userIdentifiersToResponses[$userIdentifier] = $apiResponseAsArray;
        });

        // =========================================================================

        // Add all request to queue
        foreach ($urlsArray as $userUrlIdentifier => $url) {
            $request = new \cURL\Request($url);
            $requestUidToUserUrlIdentifiers[$request->getUID()] = $userUrlIdentifier;
            $queue->attach($request);
        }

        // =========================================================================

        // Execute queue
        $queue->send();

        // =========================================================================

        return $userIdentifiersToResponses;
    }

答案 3 :(得分:0)

对于PHP5.5 +, mpyw/co 是最终的解决方案。它的工作方式就像JavaScript中的tj/co一样。

实施例

假设您要下载指定的多个GitHub用户&#39;化身。每个用户都需要执行以下步骤。

  1. 获取http://github.com/mpyw(GET HTML)
  2. 的内容
  3. 查找<img class="avatar" src="...">并请求(获取图片)
  4. ---:等待我的回复
    ...:在并行流程中等待其他响应

    许多着名的基于curl_multi的脚本已经为我们提供了以下流程。

            /-----------GET HTML\  /--GET IMAGE.........\
           /                     \/                      \ 
    [Start] GET HTML..............----------------GET IMAGE [Finish]
           \                     /\                      /
            \-----GET HTML....../  \-----GET IMAGE....../
    

    然而,这还不够有效。您想减少无用的等待时间...吗?

            /-----------GET HTML--GET IMAGE\
           /                                \            
    [Start] GET HTML----------------GET IMAGE [Finish]
           \                                /
            \-----GET HTML-----GET IMAGE.../
    

    是的,使用mpyw / co非常容易。有关更多详细信息,请访问存储库页面。

答案 4 :(得分:0)

在PHP 7.0&amp;如PHP exec Document中所述,Apache 2.0通过添加&#34;来重定向输出。的&安培;&GT; / dev / null&amp; &#34;在命令结束时,可以让它在后台运行,只需记住正确包装命令。

$time = microtime(true);
$command = '/usr/bin/curl -H \'Content-Type: application/json\' -d \'' . $curlPost . '\' --url \'' . $wholeUrl . '\' >> /dev/shm/request.log 2> /dev/null &';
exec($command);
echo (microtime(true) - $time) * 1000 . ' ms';

以上对我来说效果很好,只需要3ms,但是在完成工作后,需要1500ms。

$time = microtime(true);
$command = '/usr/bin/curl -H \'Content-Type: application/json\' -d \'' . $curlPost . '\' --url ' . $wholeUrl;
exec($command . ' >> /dev/shm/request.log 2> /dev/null &');
echo (microtime(true) - $time) * 1000 . ' ms';

总的来说,添加&#34; &安培;&GT; / dev / null&amp;&#34;在命令结束时可能会有所帮助,只需记住正确地执行命令即可。