TrueAsync Server

(PHP 8.6+, true_async_server 0.6+)

TrueAsync Server è un'estensione PHP nativa che esegue un server HTTP performante direttamente all'interno del processo PHP. Niente daemon separato, niente reverse proxy, niente ponte FastCGI.

Supporta da subito HTTP/1.1 e HTTP/2 sulla stessa porta TCP. La scelta del protocollo avviene tramite negoziazione ALPN (per TLS) o tramite HTTP Upgrade. HTTP/3 lavora sulla stessa porta UDP (QUIC) e viene annunciato ai client tramite l'header Alt-Svc.

WebSocket, SSE e gRPC sono già progettati attorno allo stesso modello di un unico listener con rilevamento del protocollo, ma sono ancora in lavorazione (vedi Roadmap).

php
use TrueAsync\HttpServer;
use TrueAsync\HttpServerConfig;

$server = new HttpServer(
    (new HttpServerConfig())
        ->addListener('0.0.0.0', 8080)
        ->setWorkers(4)
);

$server->addHttpHandler(function ($request, $response) {
    $response->setStatusCode(200)->setBody('Hello, World!');
});

$server->start();

Perché

L'obiettivo del server è liberare il potenziale delle applicazioni PHP concorrenti.

TrueAsync ha dato al linguaggio coroutine reali, I/O non bloccante e pool di connessioni. Perché quel potenziale si traduca in carichi di produzione, serve un server progettato fin dall'inizio attorno a questo modello: un processo a vita lunga con un event loop, in cui ogni richiesta riceve la propria coroutine e lo scheduler alterna tra di esse a ogni attesa di I/O.

TrueAsync Server è esattamente questo server. Nessuno strato intermedio fra coroutine e rete: listener, parser del protocollo, dispatcher delle richieste e handler vivono nello stesso processo e nello stesso event loop. Le connessioni al database si riusano tramite Async\Pool, opcache resta caldo tra una richiesta e l'altra, il cold start si paga una volta sola, su start().

Funzionalità

StatoFunzionalitàDettagli
ProntoHTTP/1.1Conformità completa a RFC 9112, keep-alive, pipelining (tramite llhttp, lo stesso parser usato da Node.js)
ProntoHTTP/2Multiplexing, server push (libnghttp2 ≥ 1.57, soglia per CVE-2023-44487)
ProntoHTTP/3 / QUICTrasporto UDP su libngtcp2 + libnghttp3, API QUIC TLS di OpenSSL 3.5
ProntoTLS 1.2 / 1.3OpenSSL 3.x, negoziazione ALPN, cifrari deboli disattivati
ProntoCompressionegzip (zlib-ng / zlib), Brotli, zstd: sia in uscita sia in decompressione dei corpi in ingresso, su tutti i protocolli
ProntoMultipart / upload fileParser di streaming zero-copy
ProntoContropressioneCoDel (RFC 8289), sospensione adattiva dell'accept sotto carico
ProntoStreaming del corpo della richiestaOpzionale tramite HttpRequest::readBody(); upload senza tenere il corpo in RAM
ProntosendFileInvio efficiente di file dal disco direttamente dall'handler
ProntoPool di worker integratosetWorkers(N): N thread tramite Async\ThreadPool + SO_REUSEPORT
ProntoScope per richiestaOgni handler nel proprio scope; Async\request_context() fornisce un contesto comune a tutto l'albero di coroutine della richiesta
ProntoCoroutine nativeIntegrazione profonda con TrueAsync: qualunque I/O bloccante nell'handler sospende la coroutine, non il thread
ProntoZero-copyAllocazioni minime sul percorso caldo
In roadmapWebSocketRFC 6455, Upgrade da HTTP/1.1 e HTTP/2
In roadmapSSEServer-Sent Events
In roadmapgRPCSopra HTTP/2, unary e streaming

Architettura: event loop a thread singolo

Lo stesso modello usato da NGINX, Envoy, Node.js e da Rust Tokio/hyper.

Un solo thread possiede sia la connessione sia la richiesta, dall'accept fino al send. Nessun passaggio fra accept-thread e worker-thread, nessun lock, nessun cambio di contesto fra di loro. Un unico event loop accetta la connessione, legge i byte dal socket, fa il parsing HTTP, smista la richiesta all'handler e scrive la risposta, senza mai lasciare il thread.

       ┌─────────────────────────────────────────┐
       │              Event Loop Thread          │
       │                                         │
accept ─►  parse  ─►  dispatch  ─►  respond      │
       │     ▲                        │          │
       │     └──── coroutine yield ◄──┘          │
       └─────────────────────────────────────────┘

L'I/O non bloccante è gestito dal reactor libuv (tramite TrueAsync). Quando una coroutine deve attendere un file, il database o il prossimo frame WebSocket, cede il controllo all'event loop, che sceglie subito il prossimo evento pronto. Il thread non resta mai fermo in read()/recv().

Per scalare sui core si attiva la modalità multi-worker tramite setWorkers(N): l'Async\ThreadPool integrato avvia N thread OS, ciascuno con il proprio event loop indipendente, e SO_REUSEPORT (Linux/BSD) lascia che sia il kernel a distribuire le connessioni in ingresso fra di loro. Nessuno stato condiviso, nessun lock globale.

Da dove iniziare

Riferimento API

Alternative

FrankenPHP è un server embeddable separato basato su Caddy/Go, in cui PHP funge da worker. Conveniente quando servono le funzionalità di Caddy (Let's Encrypt automatico, configurazione tramite Caddyfile) o l'integrazione in un'infrastruttura Caddy esistente. TrueAsync Server è l'alternativa nativa senza runtime Go: il server vive direttamente nel processo PHP.