web-dev-qa-db-fra.com

Guzzle async demandes pas vraiment async?

Problème

Nous essayons de faire des demandes asynchrones simultanées en utilisant guzzle. Après avoir parcouru quelques ressources, comme this et this , nous avons trouvé un code qui est partagé ci-dessous. Cependant, cela ne fonctionne pas comme prévu.

Il semble que Guzzle effectue ces requêtes de manière synchrone plutôt qu'asynchrone.

Attente

Juste à des fins de test, nous frappons une URL interne, qui fait un sommeil de 5 secondes. Avec une concurrence de 10, nous nous attendons à ce que les 10 demandes soient initialement mises en file d'attente et envoyées au serveur presque simultanément, où elles attendront 5 secondes, et va alors presque tous ceux-ci se termineront presque en même temps. Ce qui inciterait le client à récupérer 10 nouvelles demandes de l'itérateur, etc.

Code

    $iterator = function() {
        $index = 0;
        while (true) {
            $client = new Client(['timeout'=>20]);
            $url = 'http://localhost/wait/5' . $index++;
            $request = new Request('GET',$url, []);
            echo "Queuing $url @ " . (new Carbon())->format('Y-m-d H:i:s') . PHP_EOL;
            yield $client
                ->sendAsync($request)
                ->then(function(Response $response) use ($request) {
                    return [$request, $response];
                });
        }
    };

    $promise = \GuzzleHttp\Promise\each_limit(
        $iterator(),
        10,  /// concurrency,
        function($result, $index) {
            /** GuzzleHttp\Psr7\Request $request */
            list($request, $response) = $result;
            echo (string) $request->getUri() . ' completed '.PHP_EOL;
        },
        function(RequestException $reason, $index) {
            // left empty for brevity
        }
    );
    $promise->wait();

Résultats actuels

Nous constatons que Guzzle n'a jamais fait de deuxième demande tant que la première n'est pas terminée. etc.

Queuing http://localhost/wait/5/1 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/2 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/3 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/4 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/5 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/6 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/7 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/8 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/9 @ 2017-09-01 17:15:28
Queuing http://localhost/wait/5/10 @ 2017-09-01 17:15:28
http://localhost/wait/5/1 completed
Queuing http://localhost/wait/5/11 @ 2017-09-01 17:15:34
http://localhost/wait/5/2 completed
Queuing http://localhost/wait/5/12 @ 2017-09-01 17:15:39
http://localhost/wait/5/3 completed
Queuing http://localhost/wait/5/13 @ 2017-09-01 17:15:45
http://localhost/wait/5/4 completed
Queuing http://localhost/wait/5/14 @ 2017-09-01 17:15:50 

Informations sur le système d'exploitation/la version

  • Ubuntu
  • PHP/7.1.3
  • GuzzleHttp/6.2.1
  • curl/7.47.0

Le problème pourrait être avec\GuzzleHttp\Promise\each_limit .. qui n'initie ou ne résout peut-être pas la promesse assez rapidement. Il est possible que nous devions tromper cela dans ticking en externe.

11
Scalable

Dans l'exemple de code, vous créez un nouveau GuzzleHttp\Client instance pour chaque demande que vous souhaitez faire. Cela peut ne pas sembler important, cependant, lors de l'instanciation de GuzzleHttp\Client il définira une valeur par défaut gestionnaire si aucun n'est fourni. (Cette valeur est ensuite transmise à toute demande envoyée via le client, sauf si elle est remplacée.)

Remarque: Il détermine le meilleur gestionnaire à utiliser à partir de la fonction this . Cependant, il finira très probablement par défaut par curl_mutli_exec.

Quelle est l'importance de cela? C'est le gestionnaire sous-jacent qui est responsable du suivi et de l'exécution de plusieurs demandes en même temps. En créant un nouveau gestionnaire à chaque fois, aucune de vos demandes n'est correctement regroupée et exécutée ensemble. Pour en savoir plus, jetez un œil au curl_multi_exec documents .

Donc, vous avez en quelque sorte deux façons de gérer cela:

Passez par le client jusqu'à l'itérateur:

$client = new GuzzleHttp\Client(['timeout' => 20]);

$iterator = function () use ($client) {
    $index = 0;
    while (true) {
        if ($index === 10) {
            break;
        }

        $url = 'http://localhost/wait/5/' . $index++;
        $request = new Request('GET', $url, []);

        echo "Queuing $url @ " . (new Carbon())->format('Y-m-d H:i:s') . PHP_EOL;

        yield $client
            ->sendAsync($request)
            ->then(function (Response $response) use ($request) {
                return [$request, $response];
            });

    }
};

$promise = \GuzzleHttp\Promise\each_limit(
    $iterator(),
    10,  /// concurrency,
    function ($result, $index) {
        /** @var GuzzleHttp\Psr7\Request $request */
        list($request, $response) = $result;
        echo (string)$request->getUri() . ' completed ' . PHP_EOL;
    }
);
$promise->wait();

ou créez le gestionnaire ailleurs et transmettez-le au client: (même si je ne sais pas pourquoi vous le feriez, mais il est là!)

$handler = \GuzzleHttp\HandlerStack::create();

$iterator = function () use ($handler) {
    $index = 0;
    while (true) {
        if ($index === 10) {
            break;
        }

        $client = new Client(['timeout' => 20, 'handler' => $handler])
        $url = 'http://localhost/wait/5/' . $index++;
        $request = new Request('GET', $url, []);

        echo "Queuing $url @ " . (new Carbon())->format('Y-m-d H:i:s') . PHP_EOL;

        yield $client
            ->sendAsync($request)
            ->then(function (Response $response) use ($request) {
                return [$request, $response];
            });

    }
};

$promise = \GuzzleHttp\Promise\each_limit(
    $iterator(),
    10,  /// concurrency,
    function ($result, $index) {
        /** @var GuzzleHttp\Psr7\Request $request */
        list($request, $response) = $result;
        echo (string)$request->getUri() . ' completed ' . PHP_EOL;
    }
);
$promise->wait();
11
Adam Lavin