Last active
November 1, 2023 13:08
-
-
Save tonysm/67a7e9a592aa8883019d8f51ae8ae39e to your computer and use it in GitHub Desktop.
Concurrent Requests
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. |
Good guesses. Those should be right. However, we now already have concurrent requests built into Laravel's HTTP Client (docs), so this snippet is not needed anymore.
Thank you!
@tonysm Using the HTTP Client how do you set the maximum concurrent requests value?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello, I have some questions:
GuzzleHttp\Pool
andGuzzleHttp\Client
classes?Illuminate\Http\Client\Response
class?GuzzleHttp\Exception\RequestException
class?Illuminate\Http\Client\PendingRequest
class?Illuminate\Support\Facades\Http
class?Thanks for sharing!