Server ad alte prestazioni con Swoole
Gestite migliaia di connessioni con Swoole basato sulle coroutine.
Server ad alte prestazioni con Swoole è una lezione PHP Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento PHP Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso PHP Academy include 4 lezioni in totale.
Cos'è Swoole
Swoole (e il suo fork OpenSwoole) è un'estensione C che trasforma PHP in un runtime ad alte prestazioni, basato su coroutine e guidato dagli eventi. Invece di creare un processo per ogni richiesta come fa PHP-FPM, un server Swoole avvia l'applicazione una sola volta e la mantiene residente in memoria, servendo le richieste da un pool di worker di lunga durata con coroutine integrate. Il risultato è un aumento del throughput di un ordine di grandezza e la concorrenza nativa all'interno di una singola richiesta.
Il server HTTP persistente
L'esempio classico: un server HTTP completo racchiuso in uno script. Resta in ascolto, inoltra ogni richiesta a un worker e non esegue mai nuovamente il bootstrap del framework. Tenga presente che è necessaria ext-swoole: il server non funziona con il PHP CLI standard.
<?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();Coroutine, non callback
Il punto di forza di Swoole sono le coroutine: è possibile scrivere codice sequenziale con stile bloccante, che il runtime pianifica in modo cooperativo. Coroutine\run() apre un contesto di coroutine; go() ne avvia una nuova. Ogni coroutine ha il proprio stack, come un Fiber, ma Swoole le pianifica automaticamente nei punti di I/O.
<?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
});Funzioni bloccanti intercettate
Il meccanismo che rende ergonomiche le coroutine sono gli hook di runtime. Swoole riscrive in modo trasparente le chiamate bloccanti — PDO, Redis, curl, sleep, file_get_contents — trasformandole in versioni non bloccanti compatibili con le coroutine. Una normale chiamata $pdo->query() cede il controllo alla coroutine invece di bloccare il 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 hookedI/O concorrente con WaitGroup
Per distribuire il lavoro e riunire i risultati, Swoole offre un WaitGroup (simile a sync.WaitGroup di Go). Avvii le coroutine, chiami add() per ogni attività, done() al completamento e wait() per attendere che terminino tutte, in modo concorrente.
<?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);
});Canali per la comunicazione
Le coroutine coordinano il proprio lavoro tramite i Canali: code tipizzate e con capacità limitata che sospendono il produttore quando sono piene e il consumatore quando sono vuote. Si tratta di messaggistica in stile CSP, che evita completamente i lock sullo stato condiviso.
<?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";
});
});Pool di connessioni
Un server persistente non dovrebbe aprire una connessione al database per ogni richiesta. Swoole offre pool di connessioni sicuri per le coroutine, consentendo ai worker di riutilizzare un insieme fisso di connessioni tra migliaia di coroutine concorrenti ed eliminando il continuo ricambio delle connessioni.
<?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);
});
});Perdita di stato: la trappola della memoria persistente
Poiché il processo rimane attivo tra una richiesta e l'altra, lo stato globale e statico persiste. Una cache statica che cresce senza limiti, un singleton che conserva dati specifici della richiesta o una transazione DB lasciata aperta possono compromettere le richieste successive ed esaurire la memoria. Regole: eviti i dati specifici della richiesta in statici e singleton, reimposti il contesto per ogni richiesta e imposti max_request affinché i worker vengano ricreati periodicamente.
<?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!Contesto locale alla coroutine
Poiché un singolo worker alterna l'esecuzione di molte coroutine, non è possibile usare normali statici per i dati specifici della richiesta. Swoole fornisce a ogni coroutine un contesto (Coroutine::getContext()): un oggetto la cui durata coincide con quella della coroutine, ideale per l'utente corrente, l'ID della richiesta o l'handle della transazione.
<?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;
});
});Modello dei processi e task worker
Swoole esegue un processo master (che gestisce i thread del ciclo degli eventi), un manager (che avvia e monitora i worker), processi worker (che eseguono i gestori delle richieste e le coroutine) e, facoltativamente, task worker per i lavori pesanti, bloccanti o vincolati dalla CPU, delegati tramite $server->task(). Utilizzi i task worker per l'elaborazione delle immagini o per i lavori CPU-intensive di lunga durata, così non rallenteranno le coroutine delle richieste: otterrà sia concorrenza sia parallelismo.
Defer e timer
Swoole consente di pianificare il lavoro dopo il completamento della coroutine corrente con Coroutine::defer(): è l'ideale per rilasciare in modo affidabile le risorse dei pool anche in caso di eccezione. I timer a livello di server (Timer::tick) eseguono attività periodiche, come l'aggiornamento della cache, all'interno di un worker senza ricorrere a un cron esterno.
<?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";
});
});Verifica rapida
Perché è necessario evitare di memorizzare i dati della richiesta in proprietà statiche in un server Swoole?
Ripasso
Swoole trasforma PHP in un server residente guidato dalle coroutine:
- L'applicazione viene avviata una sola volta; un pool di worker serve le richieste con coroutine integrate (
Co\run/go()). - Gli hook di runtime rendono non bloccanti le normali operazioni PDO, curl e sleep; WaitGroup e Canali coordinano il lavoro concorrente.
- I pool di connessioni riutilizzano le connessioni DB tra le coroutine.
- La memoria persistente impone di evitare le perdite di stato: utilizzi
Coroutine::getContext()per i dati specifici della richiesta, impostimax_requeste deleghi il lavoro pesante ai task worker.
Impara PHP con un tutor IA — gratis
Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.
- Corsi
- 49
- Lezioni
- 195
Domande Frequenti
La lezione «Server ad alte prestazioni con Swoole» è gratuita?
Sì — il testo completo di «Server ad alte prestazioni con Swoole» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso PHP Academy, passa a CoddyKit PRO. Il corso PHP Academy include 4 lezioni in totale.
Cosa imparerò in «Server ad alte prestazioni con Swoole»?
Gestite migliaia di connessioni con Swoole basato sulle coroutine. Eserciti PHP Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare PHP Academy?
Non è richiesta alcuna esperienza precedente. PHP Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Server ad alte prestazioni con Swoole»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione PHP Academy?
Sì. Ogni lezione PHP Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Il modello di concorrenza di PHP
- Fibers di PHP 8.1
- Event loop con ReactPHP
- Server ad alte prestazioni con Swoole