-
-
Save fgilio/7bc0d5dce3f1b3670f2d14a334486d7b to your computer and use it in GitHub Desktop.
Laravel HTTP client concurrent requests pool
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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