0Pricing
PHP Academy · Lesson

High-Performance Servers with Swoole

Serve thousands of connections with coroutine-based Swoole.

High-Performance Servers with Swoole is a free PHP Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the PHP Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Swoole Is

Swoole (and its fork OpenSwoole) is a C extension that turns PHP into a high-performance, coroutine-based, event-driven runtime. Instead of PHP-FPM spawning a process per request, a Swoole server boots your app once and keeps it resident in memory, serving requests from a pool of long-lived workers with built-in coroutines. The result: order-of-magnitude throughput gains and native concurrency inside a single request.

The Persistent HTTP Server

The classic example: a full HTTP server in a script. It listens, dispatches each request to a worker, and never re-bootstraps the framework. Note this requires ext-swoole — it does not run on stock PHP CLI.

<?php
$server = new Swoole\Http\Server('0.0.0.0', 9501);
$server->set(['worker_num' => 4]);

$server->on('request', function ($request, $response) {
    $response->header('Content-Type', 'application/json');
    $response->end(json_encode(['hello' => $request->server['request_uri']]));
});

echo "listening on :9501\n";
$server->start();

Coroutines, Not Callbacks

Swoole's killer feature is coroutines: write blocking-style sequential code that the runtime schedules cooperatively. Coroutine\run() opens a coroutine context; go() launches a new coroutine. Each coroutine has its own stack, like a Fiber, but Swoole schedules them automatically at I/O points.

<?php
use Swoole\Coroutine as Co;

Co\run(function () {
    go(function () { Co::sleep(1); echo "A done\n"; });
    go(function () { Co::sleep(1); echo "B done\n"; });
    // Both sleep concurrently -> total ~1s, not 2s
});

Hooked Blocking Functions

The magic that makes coroutines ergonomic: runtime hooks. Swoole transparently rewrites blocking calls — PDO, Redis, curl, sleep, file_get_contents — into non-blocking coroutine-aware versions. Your ordinary $pdo->query() yields the coroutine instead of freezing the worker.

<?php
use Swoole\Runtime;
use Swoole\Coroutine as Co;

Runtime::enableCoroutine(SWOOLE_HOOK_ALL); // hook PDO, curl, sleep, etc.

Co\run(function () {
    go(function () { sleep(1); echo "task 1\n"; }); // sleep() is now async!
    go(function () { sleep(1); echo "task 2\n"; });
}); // finishes in ~1s because sleep was hooked

Concurrent I/O with WaitGroup

To fan out work and join, Swoole offers a WaitGroup (like Go's sync.WaitGroup). Launch coroutines, add() per task, done() on completion, and wait() to block until all finish — concurrently.

<?php
use Swoole\Coroutine as Co;
use Swoole\Coroutine\WaitGroup;

Co\run(function () {
    $wg = new WaitGroup();
    $results = [];
    foreach (['eu', 'us', 'asia'] as $region) {
        $wg->add();
        go(function () use ($wg, $region, &$results) {
            Co::sleep(0.5);              // simulated API call
            $results[$region] = "ok-$region";
            $wg->done();
        });
    }
    $wg->wait();                         // all three ran in parallel (~0.5s)
    var_dump($results);
});

Channels for Communication

Coroutines coordinate via Channels — typed, bounded queues that suspend the producer when full and the consumer when empty. This is CSP-style messaging, avoiding shared-state locks entirely.

<?php
use Swoole\Coroutine as Co;
use Swoole\Coroutine\Channel;

Co\run(function () {
    $chan = new Channel(2);
    go(function () use ($chan) {
        foreach (range(1, 3) as $n) $chan->push($n);
        $chan->push(null); // sentinel
    });
    go(function () use ($chan) {
        while (($v = $chan->pop()) !== null) echo "got $v\n";
    });
});

Connection Pooling

A persistent server should not open a DB connection per request. Swoole provides coroutine-safe connection pools so workers reuse a fixed set of connections across thousands of concurrent coroutines, eliminating connection churn.

<?php
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Swoole\Coroutine as Co;

Co\run(function () {
    $pool = new PDOPool((new PDOConfig())
        ->withHost('127.0.0.1')->withDbname('app')
        ->withUsername('u')->withPassword('p'), 8);

    go(function () use ($pool) {
        $pdo = $pool->get();           // borrow
        $row = $pdo->query('SELECT NOW()')->fetch();
        $pool->put($pdo);              // return to pool
        var_dump($row);
    });
});

State Leaks: The Persistent-Memory Trap

Because the process lives across requests, global and static state persists. A static cache that grows unbounded, a singleton holding request-specific data, or a leaked DB transaction will corrupt later requests and exhaust memory. Rules: avoid request-scoped data in statics/singletons, reset per-request context, and set max_request so workers recycle periodically.

<?php
$server = new Swoole\Http\Server('0.0.0.0', 9501);
$server->set([
    'worker_num'  => 8,
    'max_request' => 10000, // recycle a worker after N requests to bound leaks
]);
// Never store $request data in a static property between requests!

Coroutine-Local Context

Since one worker interleaves many coroutines, you cannot use plain statics for per-request data. Swoole gives each coroutine a context (Coroutine::getContext()) — an object whose lifetime is the coroutine, perfect for the current user, request ID, or transaction handle.

<?php
use Swoole\Coroutine as Co;

Co\run(function () {
    go(function () {
        $ctx = Co::getContext();
        $ctx['request_id'] = 'req-123';
        // ... deep call chain can read Co::getContext()['request_id']
        echo Co::getContext()['request_id'], PHP_EOL;
    });
});

Process Model and Task Workers

Swoole runs a master process (manages event loop threads), a manager (spawns/monitors workers), worker processes (run your request handlers + coroutines), and optional task workers for heavy, blocking, or CPU-bound jobs offloaded via $server->task(). Use task workers for image processing or long CPU work so they do not stall request coroutines — giving you both concurrency and parallelism.

Defer and Timers

Swoole lets you schedule work after the current coroutine finishes with Coroutine::defer() — ideal for releasing pooled resources reliably even if an exception is thrown. Server-wide timers (Timer::tick) run periodic jobs like cache refresh inside a worker without an external cron.

<?php
use Swoole\Coroutine as Co;

Co\run(function () {
    go(function () {
        $conn = 'borrowed-connection';
        Co::defer(function () use ($conn) {
            echo "released $conn\n"; // runs when coroutine exits, even on error
        });
        echo "using $conn\n";
    });
});

Quick Check

Why must you avoid storing request data in static properties in a Swoole server?

Recap

Swoole turns PHP into a resident, coroutine-driven server:

  • The app boots once; a worker pool serves requests with built-in coroutines (Co\run/go()).
  • Runtime hooks make ordinary PDO/curl/sleep non-blocking; WaitGroup and Channels coordinate concurrent work.
  • Connection pools reuse DB connections across coroutines.
  • Persistent memory means you must avoid state leaks: use Coroutine::getContext() for per-request data, set max_request, and offload heavy work to task workers.

Frequently asked questions

Is the “High-Performance Servers with Swoole” lesson free?

Yes — the full text of “High-Performance Servers with Swoole” is free to read here on the web, and the PHP Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the PHP Academy course, upgrade to CoddyKit PRO.

What will I learn in “High-Performance Servers with Swoole”?

Serve thousands of connections with coroutine-based Swoole. You practise PHP Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start PHP Academy?

No prior experience is required. PHP Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “High-Performance Servers with Swoole” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this PHP Academy lesson?

Yes. Every PHP Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The PHP Concurrency Model
  2. PHP 8.1 Fibers
  3. Event Loops with ReactPHP
  4. High-Performance Servers with Swoole
← Back to PHP Academy