Vilx-
Vilx-

Reputation: 106920

How to process GuzzleHTTP async requests without blocking?

I need to write a processor that can potentially send out many HTTP requests to an external service. Since I want to maximize performance, I wish to minimize blocking. I'm using PHP 5.6 and GuzzleHTTP.

GuzzleHTTP does have an option for async requests. But since we do have only 1 thread available in PHP, I need to allocate some time for them to be processed. Unfortunately I only see one way to do it - calling wait which blocks until all the requests are processed. That's not what I want.

Instead I'd like to have some method that handles whatever has arrived, and then returns. So that I can do something along the lines of:

$allRequests = [];

while ( !checkIfNeedToEnd() ) {
    $newItems = getItemsFromQueue();
    $allRequests = $allRequests + spawnRequests($newItems);
    GuzzleHttp::processWhatYouCan($allRequests);
    removeProcessedRequests($allRequests);
}

Is this possible?

Upvotes: 3

Views: 2231

Answers (1)

Vilx-
Vilx-

Reputation: 106920

Alright... figured it out myself:

$handler = new \GuzzleHttp\Handler\CurlMultiHandler();
$client = new \GuzzleHttp\Client(['handler' => $handler]);
$promise1 = $client->getAsync("http://www.stackoverflow.com");
$promise2 = $client->getAsync("http://localhost/");

$doneCount = 0;

$promise1->then(function() use(&$doneCount) {
    $doneCount++;
    echo 'Promise 1 done!';
});
$promise2->then(function() use(&$doneCount) {
    $doneCount++;
    echo 'Promise 2 done!';
});

$last = microtime(true);
while ( $doneCount < 2 ) {
    $now = microtime(true);
    $delta = round(($now-$last)*1000);
    echo "tick($delta) ";
    $last = $now;

    $handler->tick();
}

And the output I get is:

tick(0) tick(6) tick(1) tick(0) tick(1001) tick(10) tick(96) Promise 2 done!tick(97) Promise 1 done!

The magic ingredient is creating the CurlMultiHandler yoursef and then calling tick() on that when it's convenient. After that it's promises as usual. And if the queue is empty, tick() returns immediately.

Note that it can still block for up to 1 second (default) if there is no activity. This can be also changed if needed:

$handler = new \GuzzleHttp\Handler\CurlMultiHandler(['select_timeout' => 0.5]);

The value is in seconds, but with floating point.

Upvotes: 6

Related Questions