Put each Guzzle job's proxy on the asynchronous operation started by the pool. Within an existing client setup, use promise factories:

$operations = function () use ($client, $jobs) {
    foreach ($jobs as $id => $job) {
        yield $id => function () use ($client, $job) {
            return $client->requestAsync('GET', $job['url'], [
                'proxy' => $job['proxy'],
                'timeout' => 20,
            ]);
        };
    }
};

$pool = new GuzzleHttp\Pool($client, $operations(), [
    'concurrency' => 4,
]);
$pool->promise()->wait();

Each validated job supplies a destination and proxy. For an allowed public request, an HTTP candidate from the HTTP proxy list can supply that job's credential-free proxy URL; selection happens before its factory runs. A PSR-7 request describes the HTTP message, while these send options control the transport. Printing or storing a proxy value beside the message cannot configure an operation that sends it another way.

Return requestAsync() from the factory. Calling synchronous request() there can block while the pool is trying to start concurrent work.

Add fulfilled and rejected callbacks using the application's existing handling. Keep the job key, selected proxy identifier and attempt number in both outcomes; completion order differs from input order. Do not secretly choose another endpoint in the factory after recording one for the job.

The concurrency value bounds this pool's work. Several jobs selecting one constrained proxy or account can still require a separate budget for that endpoint.

Interpret results by stage: destination rejection, proxy authentication and connection timeout are different outcomes. The send options retained with the job show which route produced each result.

Mikanda mikatakata