Apache Kafka con PHP
Elabori eventi ad alta velocità con Kafka
Apache Kafka con PHP è una lezione PHP Academy gratuita su CoddyKit. Questa è la lezione 3 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.
Kafka con PHP
Kafka non è una coda di attività: è un commit log distribuito e append-only. I producer aggiungono record ai topic; i consumer leggono dal proprio offset e possono rileggere la cronologia. Questo rende Kafka ideale per flussi di eventi ad alto throughput, event sourcing e alimentazione di più gruppi di consumer indipendenti a partire da un unico stream.
In PHP si comunica con Kafka tramite ext-rdkafka, un binding sulla libreria C collaudata librdkafka.
Log, non coda
Il cambio di prospettiva mentale da RabbitMQ a Kafka:
- I messaggi non vengono eliminati quando sono consumati; scadono in base alla politica di conservazione (tempo o dimensione).
- Ogni consumer tiene traccia del proprio offset, cioè della sua posizione nel log.
- Un topic è suddiviso in partizioni; l'ordinamento è garantito solo all'interno di una partizione.
- Più gruppi di consumer leggono lo stesso topic in modo indipendente.
Se servono il replay, la distribuzione a molti lettori o un throughput enorme, Kafka è adatto. Se servono il routing per messaggio e i TTL, è adatto RabbitMQ.
Installazione di ext-rdkafka
Installi prima la libreria nativa, poi l'estensione PECL e, facoltativamente, un wrapper di livello superiore.
# Debian/Ubuntu
apt-get install -y librdkafka-dev
pecl install rdkafka
echo "extension=rdkafka.so" >> php.ini
# Optional ergonomic wrapper
composer require longlang/phpkafka # or arnaud-lb/php-rdkafka-stubs for IDEProduzione dei record
Crei un RdKafka\Producer, ottenga un handle per il topic e chiami produce(). La chiave determina in quale partizione finirà un record: stessa chiave, stessa partizione e ordine preservato. Esegua sempre flush() prima di uscire, altrimenti i record bufferizzati andranno persi.
<?php
$conf = new RdKafka\Conf();
$conf->set('bootstrap.servers', 'localhost:9092');
$producer = new RdKafka\Producer($conf);
$topic = $producer->newTopic('orders');
// RD_KAFKA_PARTITION_UA = let the partitioner choose by key
$key = 'order-42';
$topic->produce(RD_KAFKA_PARTITION_UA, 0, json_encode(['id' => 42]), $key);
$producer->poll(0);
$result = $producer->flush(10000); // wait up to 10s
if ($result !== RD_KAFKA_RESP_ERR_NO_ERROR) {
throw new RuntimeException('Failed to flush');
}Partizionamento per chiave
Il partizionamento è il cuore della scalabilità e dell'ordinamento di Kafka. Il partizionatore predefinito calcola l'hash della chiave del record: partition = hash(key) % numPartitions. La scelta di una buona chiave è importante:
- Chiave basata su
customerId→ tutti gli eventi di un cliente restano ordinati e in un'unica partizione. - Chiave nulla → distribuzione round-robin tra le partizioni (throughput massimo, nessun ordinamento).
Non è mai possibile ridurre il numero di partizioni di un topic e l'aggiunta di partizioni rimescola la mappatura dell'hash: dimensioni quindi le partizioni in anticipo per il massimo parallelismo.
Gruppi di consumer e offset
Utilizzi il KafkaConsumer di alto livello con group.id. Kafka assegna le partizioni ai membri del gruppo ed esegue un ribilanciamento quando i membri entrano o escono. Ogni membro legge solo le partizioni assegnate, ottenendo così scalabilità orizzontale senza costi aggiuntivi.
<?php
$conf = new RdKafka\Conf();
$conf->set('bootstrap.servers', 'localhost:9092');
$conf->set('group.id', 'order-emailers');
$conf->set('auto.offset.reset', 'earliest'); // start of log if no offset
$conf->set('enable.auto.commit', 'false'); // we commit manually
$consumer = new RdKafka\KafkaConsumer($conf);
$consumer->subscribe(['orders']);
while (true) {
$msg = $consumer->consume(5000);
if ($msg->err === RD_KAFKA_RESP_ERR_NO_ERROR) {
handle($msg->payload);
$consumer->commit($msg); // commit offset AFTER work
}
}
function handle(string $p): void {}Quando eseguire il commit
Il commit dell'offset definisce la semantica di consegna:
- Commit dopo l'elaborazione → at-least-once (un arresto anomalo prima del commit riproduce il record).
- Commit prima dell'elaborazione → at-most-once (un arresto anomalo lo fa perdere).
L'auto-commit (enable.auto.commit=true) esegue il commit a intervalli regolari, indipendentemente dal fatto che l'elaborazione sia terminata: è comodo, ma un arresto anomalo può eliminare i record senza che ve ne accorgiate. Lo disabiliti ed esegua manualmente il commit quando la correttezza è importante.
Gestione degli errori di consumo
Non ogni valore restituito da consume() è un messaggio. È necessario distinguere in base al codice di errore: __PARTITION_EOF e __TIMED_OUT sono normali segnali di controllo, non errori.
<?php
$msg = $consumer->consume(2000);
switch ($msg->err) {
case RD_KAFKA_RESP_ERR_NO_ERROR:
echo "Got: {$msg->payload} @ offset {$msg->offset}\n";
break;
case RD_KAFKA_RESP_ERR__PARTITION_EOF:
echo "Reached end of partition\n"; // caught up, keep polling
break;
case RD_KAFKA_RESP_ERR__TIMED_OUT:
echo "No message this poll\n";
break;
default:
throw new \Exception($msg->errstr(), $msg->err);
}Ottimizzazione del throughput
Il throughput di Kafka deriva dal batching. Impostazioni chiave del producer:
linger.ms— attende brevemente per raggruppare più record in ogni richiesta (ad es. 5–20 ms).batch.size/queue.buffering.max.messages— buffer più grandi, meno richieste avanti e indietro.compression.type—lz4ozstdriduce drasticamente il costo di rete.acks—allper la durabilità,1per una latenza inferiore.
<?php
$conf = new RdKafka\Conf();
$conf->set('bootstrap.servers', 'localhost:9092');
$conf->set('compression.type', 'lz4');
$conf->set('linger.ms', '10');
$conf->set('batch.size', '65536');
$conf->set('acks', 'all');
$producer = new RdKafka\Producer($conf);Callback dei rapporti di consegna
Poiché produce() è asincrona, un invio fallito non genera immediatamente un'eccezione. Registri una callback del rapporto di consegna nella configurazione del producer per conoscere l'esito di ogni record: in PHP questo è l'unico modo affidabile per rilevare gli errori silenziosi del producer.
<?php
$conf = new RdKafka\Conf();
$conf->set('bootstrap.servers', 'localhost:9092');
$conf->setDrMsgCb(function ($producer, $msg) {
if ($msg->err) {
fwrite(STDERR, 'Delivery FAILED: ' . rd_kafka_err2str($msg->err) . "\n");
} else {
echo "Delivered to partition {$msg->partition} @ offset {$msg->offset}\n";
}
});
$producer = new RdKafka\Producer($conf);
// poll() services the callback queue; call it after producing
$producer->poll(0);Problemi specifici di PHP
Kafka presuppone client di lunga durata; il ciclo di vita delle richieste PHP va nella direzione opposta:
- I producer eseguono il buffering in modo asincrono: esegua sempre
flush()prima che lo script termini, altrimenti i record andranno persi. - Esegua i consumer come worker CLI persistenti sotto un supervisore, mai all'interno di una richiesta web.
- I ribilanciamenti sospendono il consumo; mantenga breve l'elaborazione per messaggio oppure imposti
max.poll.interval.mssu un valore sufficientemente alto, così da non essere espulso dal gruppo. - Imposti
log_levele registri la callback del rapporto di consegna del producer per rilevare gli errori silenziosi.
Verifica rapida
Garanzie sull'ordinamento in Kafka.
Riepilogo
Kafka in PHP:
- Kafka è un log riproducibile, non una coda; i consumer tengono traccia degli offset.
- Il partizionamento per chiave offre ordinamento per chiave e scalabilità.
- I gruppi di consumer suddividono le partizioni ed eseguono automaticamente il ribilanciamento.
- Esegua il commit degli offset dopo l'elaborazione per ottenere at-least-once; disabiliti l'auto-commit per avere il controllo.
- Regoli
linger.ms,batch.sizeecompression.type; esegua sempreflush().
Successivamente: collegheremo queste primitive per creare flussi di lavoro affidabili basati sugli eventi.
Domande Frequenti
La lezione «Apache Kafka con PHP» è gratuita?
Sì — il testo completo di «Apache Kafka con PHP» è 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 «Apache Kafka con PHP»?
Elabori eventi ad alta velocità con Kafka 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 3 di 4.
Quanto tempo richiede la lezione «Apache Kafka con PHP»?
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
- Perché usare la messaggistica asincrona
- Utilizzare RabbitMQ in PHP
- Apache Kafka con PHP
- Creare flussi di lavoro basati sugli eventi