web-dev-qa-db-fra.com

La création de requêtes HTTP asynchrones est-elle possible avec PHP?

J'ai un script PHP qui doit télécharger plusieurs fichiers à partir d'un serveur distant. Pour le moment, une boucle est en cours de téléchargement et de traitement des fichiers avec cURL, ce qui signifie que le téléchargement d’un fichier n’a pas commencé avant la fin du précédent. Cela augmente considérablement la durée d’exécution du script.

Serait-il possible de démarrer plusieurs instances de cURL, par exemple, pour télécharger ces fichiers de manière asynchrone en même temps sans attendre la fin de la précédente? Si oui, comment cela serait-il accompli?

35
MarathonStudios

Oui. 

Il existe la bibliothèque multirequest PHP (ou voir: projet de code Google archivé ). C'est une bibliothèque CURL multi-threadée.

Comme autre solution, vous pouvez écrire un script qui le fait dans un langage qui prend en charge les threads, comme Ruby ou Python. Ensuite, appelez simplement le script avec PHP. Cela semble assez simple.

17
bl00dshooter

Découvrez curl-easy . Il prend en charge les demandes bloquantes et non bloquantes en parallèle ou les demandes uniques à la fois. En outre, il est testé par unité, contrairement à beaucoup de bibliothèques simples ou boguées.

Divulgation: Je suis l'auteur de cette bibliothèque. La bibliothèque a sa propre suite de tests, je suis donc plutôt convaincu qu'elle est robuste.

Consultez également l'exemple d'utilisation ci-dessous:

<?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();
21
stil

La bibliothèque de @stil est vraiment cool. Merci beaucoup à lui!

Malgré tout, j’ai écrit la fonction utilitaire Nice qui permet très facilement d’obtenir du contenu de manière asynchrone à partir de plusieurs URL (API dans mon cas) et de les renvoyer sans perdre les informations qui sont.

Vous l'exécutez simplement en passant le tableau key => value en entrée et il renvoie le tableau key => response: -)

/** 
     * 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; "[email protected]" =>
     * &nbsp; "http://someapi.com/results?search=easylife",<br />
     * &nbsp; "[email protected]" =>
     * &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; "[email protected]" => <br />
     * &nbsp; "Work less, enjoy more",<br />
     * &nbsp; "[email protected]" => <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;
    }
1
Rav

Pour PHP 5.5 +, mpyw/co est la solution ultime. Cela fonctionne comme s'il s'agissait de tj/co en JavaScript.

Exemple

Supposons que vous souhaitiez télécharger les avatars de plusieurs utilisateurs GitHub spécifiés. Les étapes suivantes sont requises pour chaque utilisateur.

  1. Obtenir le contenu de http://github.com/mpyw (GET HTML)
  2. Trouvez <img class="avatar" src="..."> et demandez-le (OBTENIR UNE IMAGE)

---: En attente de ma réponse
...: Attente d'une autre réponse dans des flux parallèles

De nombreux scripts basés sur curl_multi nous fournissent déjà les flux suivants.

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

Cependant, ce n'est pas assez efficace. Voulez-vous réduire les temps d'attente sans valeur ...?

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

Oui, c'est très facile avec mpyw/co. Pour plus de détails, visitez la page du référentiel.

0
mpyw

Dans PHP 7.0 et Apache 2.0, comme indiqué dans PHP exec Document , redirigez le résultat, en ajoutant "&>/dev/null &" à la fin du texte. commande, pourrait le faire fonctionner en arrière-plan, n'oubliez pas d'envelopper la commande correctement.

$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';

Ci-dessus fonctionne bien pour moi, ne prend que 3 ms, mais cela ne fonctionnera pas, cela prendra 1500 ms.

$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';

Au total, l'ajout de "&>/dev/null &" à la fin de votre commande peut aider, mais n'oubliez pas de bien enrouler votre commande.

0
user5175717