Guzzle async-Anfragen nicht wirklich async?

Lesezeit: 5 Minuten

Benutzeravatar von Scalable
Skalierbar

Problem

Wir versuchen, gleichzeitige asynchrone Anfragen mit guzzle durchzuführen. Nachdem Sie ein paar Ressourcen durchgegangen sind, z Das Und Das, haben wir uns einen Code ausgedacht, der unten geteilt wird. Allerdings funktioniert es nicht wie erwartet.

Es sieht so aus, als würde Guzzle diese Anfrage eher synchron als asynchron ausführen.

Erwartung

Nur zu Testzwecken treffen wir auf eine interne URL, die einen 5-Sekunden-Schlaf ausführt. Bei einer Parallelität von 10 erwarten wir, dass zunächst alle 10 Anfragen in die Warteschlange gestellt und an den Server gesendet werden fast gleichzeitig, wo sie 5 Sekunden warten und dann fast alle werden fast gleichzeitig fertig sein. Was den Guzzle-Client dazu bringen würde, 10 neue Anfragen vom Iterator entgegenzunehmen und so weiter.

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();

Tatsächliche Ergebnisse

Wir stellen fest, dass Guzzle nie eine zweite Anfrage gestellt hat, bis die erste fertig ist. usw.

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 

Betriebssystem-/Versionsinformationen

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

Das Problem könnte bei \GuzzleHttp\Promise\each_limit .. liegen, was das Promise vielleicht nicht schnell genug initiiert oder auflöst. Es kann sein, dass wir das austricksen müssen ticknach außen gehen.

Im Beispielcode erstellen Sie eine neue GuzzleHttp\Client Instanz für jede Anfrage, die Sie stellen möchten. Dies scheint jedoch während der Instanziierung von nicht wichtig zu sein GuzzleHttp\Client es wird eine Standardeinstellung festlegen Handler wenn keine angegeben ist. (Dieser Wert wird dann an jede Anfrage weitergegeben, die über den Client gesendet wird, es sei denn, er wird außer Kraft gesetzt.)

Hinweis: Es bestimmt den besten zu verwendenden Handler Das Funktion. Es wird jedoch höchstwahrscheinlich standardmäßig darauf hinauslaufen curl_mutli_exec.

Was ist die Bedeutung davon? Es ist der zugrunde liegende Handler, der für das gleichzeitige Verfolgen und Ausführen mehrerer Anforderungen verantwortlich ist. Indem Sie jedes Mal einen neuen Handler erstellen, wird keine Ihrer Anfragen richtig gruppiert und zusammen ausgeführt. Um mehr darüber zu erfahren, werfen Sie einen Blick in die curl_multi_exec Dokumente.

Sie haben also zwei Möglichkeiten, damit umzugehen:

Übergeben Sie den Client bis zum Iterator:

$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();

oder erstellen Sie den Handler an anderer Stelle und übergeben Sie ihn an den Client: (Ich bin mir zwar nicht sicher, warum Sie das tun würden, aber er ist da!)

$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();

  • Das Erstellen eines neuen Clients ist wichtig, weil Anforderungen die Verwendung von Proxy-Servern und anderen anforderungsspezifischen Parametern vorschreiben. Das bedeutet, dass jede Anfrage eine völlig andere Anfrage sein kann, einschließlich URL, Header und Methode. Wenn ich eine einzelne Clientinstanz erstelle, weiß ich nicht, was sie mit anderen Anfragen teilt. Mit dieser Logik ist wahrscheinlich die zweite von Ihnen vorgeschlagene Lösung vorzuziehen. Ich werde beide oben vorgeschlagenen Lösungen ausprobieren und berichten. Schade, dass ich gerade zu Hause bin … und nicht die Einrichtung habe, um das oben Gesagte zu versuchen.

    – Skalierbar

    3. September 2017 um 15:00 Uhr


  • Das solltest du auf deine Anfrage mit einstellen können proxy Möglichkeit, docs.guzzlephp.org/en/stable/request-options.html#proxy.

    – Adam Lavan

    3. September 2017 um 15:04 Uhr

  • Indem Sie jedes Mal einen neuen Handler erstellen, wird keine Ihrer Anfragen richtig gruppiert Warum ist das ein Problem? Warum können sie nicht in getrennten Gruppen zusammenlaufen? Bedeutet das, dass wenn ich nur eine Anfrage mache, diese immer synchron ausgeführt wird?

    – Draex_

    3. April 2019 um 7:26 Uhr


  • Ich bin hierher gekommen, um herauszufinden, dass php-curl ein Muss ist, um asynchron zu laufen. Ich hatte es nicht installiert und verbrachte 2 Tage damit, das Problem zu finden.

    – Str

    10. Juli 2019 um 16:58 Uhr

  • @AdamLavin Das Bereitstellen von Links zu bestimmten Dateien (und sogar Zeilen!) im Master-Zweig ist keine gute Idee – es ist mit ziemlicher Sicherheit garantiert, dass sie im Laufe der Zeit veraltet sein werden. Eine bessere Option ist die Verwendung des Revisions-Hashs anstelle des Zweignamens

    – warum

    21. Januar 2021 um 17:55 Uhr


1444780cookie-checkGuzzle async-Anfragen nicht wirklich async?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy