Skip to content
Muhammet Şafak
tr
Asked by: Berk Answered:

For concurrent HTTP calls, should I reach for raw Fibers, ReactPHP/AMPHP, or Swoole?


Question

We run a Laravel service that, on a single request, has to call about 200 different upstream APIs and merge the results. Right now we do them sequentially and, unsurprisingly, the request latency is terrible. I want to make these concurrent, but I can't decide whether to use raw Fibers in PHP 8.3, an established async runtime like ReactPHP/AMPHP, or just move to Swoole. Do raw Fibers actually buy me anything over an established runtime?

Answer

Short answer: raw Fibers on their own buy you nothing here.

Short answer

For HTTP fan-out the real choice is curl_multi/Guzzle Pool if you stay in FPM, AMPHP v3 if you want ergonomic async, and Swoole only if you’re re-architecting.

Why

  1. Raw Fibers are a primitive, not a solution. A Fiber only gives you cooperative scheduling; by itself it provides neither an event loop nor non-blocking I/O. To actually run HTTP calls in parallel you’d have to write the socket layer, the scheduler, and the promise logic yourself — which is exactly what AMPHP and ReactPHP already exist to do. Dropping to raw Fibers is reinventing those libraries.

  2. AMPHP v3 = ergonomic async/await built on Fibers. It uses Fibers under the hood but gives you a clean async/await API plus amphp/http-client. No callback hell. It runs on a standard PHP install and scales when things get complex (dependent calls, streaming).

  3. ReactPHP is mature but promise/callback-style. It does the same job and has been solid in production for years. But in new code AMPHP’s async/await reads far better; for a greenfield service I’d pick AMPHP.

  4. Swoole is a different world. It needs the extension, hooks blocking I/O, and requires you to leave the FPM model for a long-running worker. Throughput is high, but so are the operational cost, the risk of state leaking between requests, and incompatibility with some extensions. I wouldn’t pay that price just for 200 calls.

What to do

  1. The boring option is usually the right one: curl_multi / Guzzle Pool. If you just need N concurrent HTTP calls, curl_multi (or Guzzle’s Pool that wraps it) does true parallel requests inside plain PHP-FPM. No new extension to install — it uses the curl extension that’s already there — no runtime swap, deployment stays identical.

  2. The real issue isn’t the runtime, it’s that number 200. 200 open connections at once punish both you and the upstreams. Set a concurrency cap of 20-50, put a timeout on every call, and handle partial failure (one bad call shouldn’t sink the batch).

    use GuzzleHttp\Client;
    use GuzzleHttp\Pool;
    use GuzzleHttp\Psr7\Request;
    
    $client = new Client(['timeout' => 3.0]);
    $requests = static function (array $urls) {
        foreach ($urls as $u) {
            yield new Request('GET', $u);
        }
    };
    
    $pool = new Pool($client, $requests($urls), [
        'concurrency' => 25, // at most 25 in flight at once
        'fulfilled'   => fn($res, $i) => $results[$i] = (string) $res->getBody(),
        'rejected'    => fn($reason, $i) => $errors[$i] = $reason, // partial failure
    ]);
    $pool->promise()->wait();

Bottom line: personally I’d stay in FPM and start with a Guzzle Pool, pin concurrency to 20-50, and handle timeouts and partial failures properly — it’s the simplest thing you can deploy today. If the codebase genuinely grows into async, I’d move to AMPHP v3. I’d only consider Swoole if moving the service to a long-running model is already on the table. And I wouldn’t reach for raw Fibers at all unless I were writing a library.

Comments

Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.

More Questions

All questions

Search the site

Start typing to search posts, projects and pages.

Esc to close Powered by Pagefind