Skip to content

Instantly share code, notes, and snippets.

@fgilio
Forked from tonysm/concurrent-pool.php
Last active February 22, 2022 13:08
Show Gist options
  • Save fgilio/7bc0d5dce3f1b3670f2d14a334486d7b to your computer and use it in GitHub Desktop.
Save fgilio/7bc0d5dce3f1b3670f2d14a334486d7b to your computer and use it in GitHub Desktop.
Laravel HTTP client concurrent requests pool
<?php
// 1. Register the routes
Route::get('test/{lorem}', function ($lorem) {
sleep(3);
return response()->json([
'message' => $lorem,
'token' => Str::random(),
]);
});
// 2. Spin up 4 `php artisan serve` processes (they will get assigned from ports 8000..8003)
// 3. Register the macro
class PendingConcurrentPool
{
private int $concurrency = 10;
private Closure $requestsBuilder;
public function __construct(Closure $requestsBuilder)
{
$this->requestsBuilder = $requestsBuilder;
}
public function concurrency(int $amount): self
{
$this->concurrency = $amount;
return $this;
}
public function wait(): Collection
{
$responses = collect();
$pool = new Pool(new Client(), call_user_func($this->requestsBuilder), [
'concurrency' => $this->concurrency,
'fulfilled' => function (Response $response, $index) use ($responses) {
$responses[$index] = new \Illuminate\Http\Client\Response($response);
},
'rejected' => function (RequestException $reason, $index) use ($responses) {
$responses[$index] = new \Illuminate\Http\Client\Response($reason->getResponse());
},
]);
$pool->promise()->wait();
return $responses;
}
}
PendingRequest::macro('pool', function (Closure $requestsBuilder) {
return new PendingConcurrentPool($requestsBuilder);
});
// 4. Paste this in your tests/Feature/ExampleTest.php
public function testSendsConcurrentRequests()
{
$responses = Http::pool(function () {
return yield from [
'req-1' => new Request('GET', 'http://localhost:8000/test/req-1'),
'req-2' => new Request('GET', 'http://localhost:8001/test/req-2'),
'req-3' => new Request('GET', 'http://localhost:8002/test/req-3'),
'req-4' => new Request('GET', 'http://localhost:8003/test/req-4'),
];
})->concurrency(4)->wait();
dump($responses->map->json());
}
// 5. The output should be something like this:
/*
Illuminate\Support\Collection^ {#670
#items: array:4 [
"req-1" => array:2 [
"message" => "req-1"
"token" => "hJXWAcsTzauf0pgM"
]
"req-2" => array:2 [
"message" => "req-2"
"token" => "plkQsQEiHDhLF90i"
]
"req-3" => array:2 [
"message" => "req-3"
"token" => "g1nPymiH4ao1BNSb"
]
"req-4" => array:2 [
"message" => "req-4"
"token" => "VB9UXvu7ekbJ1dEC"
]
]
}
*/
// Each request takes 3 seconds (see route, there is a sleep there), but since we
// are sending them all at once (we are sending 4 requests with a concurrency
// of 4), the total amount of time to send all 4 requests is 3 seconds.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment