Apache Kafka mit PHP
Events mit hohem Durchsatz mithilfe von Kafka streamen
Apache Kafka mit PHP ist eine kostenlose PHP Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des PHP Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.
Kafka mit PHP
Kafka ist keine Task-Queue, sondern ein verteiltes, nur erweiterbares Commit-Log. Producer hängen Datensätze an Topics an; Consumer lesen ab ihrem eigenen Offset und können die Historie erneut abspielen. Dadurch eignet sich Kafka ideal für Ereignisströme mit hohem Durchsatz, Event Sourcing und die Versorgung mehrerer unabhängiger Consumer-Gruppen aus einem Stream.
In PHP greifen Sie über ext-rdkafka auf Kafka zu, eine Bindung an die bewährte C-Bibliothek librdkafka.
Log statt Queue
Der gedankliche Wechsel von RabbitMQ zu Kafka:
- Nachrichten werden beim Konsumieren nicht gelöscht, sondern laufen gemäß der Aufbewahrungsrichtlinie (Zeit oder Größe) ab.
- Jeder Consumer verfolgt seinen eigenen Offset – seine Position im Log.
- Ein Topic ist in Partitionen aufgeteilt; die Reihenfolge ist nur innerhalb einer Partition garantiert.
- Mehrere Consumer-Gruppen lesen dasselbe Topic unabhängig voneinander.
Wenn Sie Wiederholungen, Fan-out an viele Leser oder enormen Durchsatz benötigen, ist Kafka geeignet. Wenn Sie Routing pro Nachricht und TTLs benötigen, ist RabbitMQ geeignet.
ext-rdkafka installieren
Installieren Sie zuerst die native Bibliothek, danach die PECL-Erweiterung und optional einen Wrapper auf höherer Ebene.
# 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 IDEDatensätze produzieren
Erstellen Sie einen RdKafka\Producer, rufen Sie ein Topic-Handle ab und verwenden Sie produce(). Der Schlüssel bestimmt, auf welcher Partition ein Datensatz landet – gleicher Schlüssel, gleiche Partition, Reihenfolge bleibt erhalten. Rufen Sie vor dem Beenden immer flush() auf, sonst gehen gepufferte Datensätze verloren.
<?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');
}Partitionierung nach Schlüssel
Die Partitionierung ist das Herzstück von Skalierbarkeit und Reihenfolge in Kafka. Der Standard-Partitioner hasht den Datensatzschlüssel: partition = hash(key) % numPartitions. Die Wahl eines guten Schlüssels ist entscheidend:
- Schlüssel nach
customerId→ alle Ereignisse eines Kunden bleiben geordnet und auf einer Partition. - Null-Schlüssel → Round-Robin über die Partitionen (maximaler Durchsatz, keine Reihenfolge).
Sie können die Anzahl der Partitionen eines Topics niemals verringern, und das Hinzufügen von Partitionen verändert die Hash-Zuordnung. Planen Sie die Partitionen daher von Anfang an für die maximale Parallelität.
Consumer-Gruppen und Offsets
Verwenden Sie den High-Level-KafkaConsumer mit group.id. Kafka verteilt die Partitionen auf die Mitglieder der Gruppe und führt ein Rebalancing durch, wenn Mitglieder hinzukommen oder wegfallen. Jedes Mitglied liest nur seine zugewiesenen Partitionen, wodurch Sie ohne zusätzlichen Aufwand horizontal skalieren können.
<?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 {}Wann committen
Der Commit des Offsets legt Ihre Zustellsemantik fest:
- Nach der Verarbeitung committen → mindestens einmal (at-least-once; ein Absturz vor dem Commit führt zu einer erneuten Zustellung des Datensatzes).
- Vor der Verarbeitung committen → höchstens einmal (at-most-once; bei einem Absturz geht der Datensatz verloren).
Auto-Commit (enable.auto.commit=true) committet nach einem Zeitplan, unabhängig davon, ob Ihre Verarbeitung abgeschlossen ist – bequem, aber bei einem Absturz können Datensätze unbemerkt verloren gehen. Deaktivieren Sie es und committen Sie manuell, wenn Korrektheit wichtig ist.
Consume-Fehler behandeln
Nicht jede Rückgabe von consume() enthält eine Nachricht. Sie müssen anhand des Fehlercodes unterscheiden – __PARTITION_EOF und __TIMED_OUT sind normale Steuersignale und keine Fehler.
<?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);
}Durchsatz optimieren
Der Durchsatz von Kafka beruht auf Batching. Wichtige Producer-Einstellungen:
linger.ms– kurz warten, um mehr Datensätze pro Anfrage zu bündeln (z. B. 5–20 ms).batch.size/queue.buffering.max.messages– größere Puffer, weniger Round-Trips.compression.type–lz4oderzstdsenkt die Netzwerkkosten deutlich.acks–allfür Persistenz,1für geringere Latenz.
<?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);Callbacks für Zustellberichte
Da produce() asynchron arbeitet, löst ein fehlgeschlagener Versand nicht sofort eine Exception aus. Registrieren Sie in der Producer-Konfiguration einen Callback für Zustellberichte, um das Schicksal jedes Datensatzes zu erfahren – dies ist in PHP die einzige zuverlässige Möglichkeit, stille Fehler des Producers zu erkennen.
<?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);PHP-spezifische Fallstricke
Kafka setzt langlebige Clients voraus; der Request-Lebenszyklus von PHP steht dem entgegen:
- Producer puffern asynchron – rufen Sie vor dem Ende des Skripts immer
flush()auf, sonst gehen Datensätze verloren. - Betreiben Sie Consumer als persistente CLI-Worker unter einem Supervisor, niemals innerhalb eines Web-Requests.
- Rebalancing unterbricht den Konsum; halten Sie Ihre Verarbeitung pro Nachricht kurz oder setzen Sie
max.poll.interval.msgroßzügig, damit Sie nicht aus der Gruppe entfernt werden. - Setzen Sie
log_levelund registrieren Sie den Callback für Zustellberichte des Producers, um stille Fehler zu erkennen.
Kurzer Check
Reihenfolgegarantien in Kafka.
Zusammenfassung
Kafka mit PHP:
- Kafka ist ein wiederabspielbares Log, keine Queue; Consumer verfolgen Offsets.
- Die Partitionierung nach Schlüssel ermöglicht Reihenfolge pro Schlüssel und Skalierung.
- Consumer-Gruppen teilen Partitionen untereinander auf und führen automatisch ein Rebalancing durch.
- Committen Sie Offsets nach der Verarbeitung für mindestens einmalige Zustellung; deaktivieren Sie Auto-Commit, um die Kontrolle zu behalten.
- Optimieren Sie
linger.ms,batch.sizeundcompression.type; rufen Sie immerflush()auf.
Als Nächstes: Diese Grundbausteine zu zuverlässigen ereignisgesteuerten Workflows verbinden.
Häufig gestellte Fragen
Ist die Lektion „Apache Kafka mit PHP“ kostenlos?
Ja — der vollständige Text von „Apache Kafka mit PHP“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des PHP Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Apache Kafka mit PHP“?
Events mit hohem Durchsatz mithilfe von Kafka streamen Du übst PHP Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um PHP Academy zu starten?
Keine Vorkenntnisse erforderlich. PHP Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.
Wie lange dauert die Lektion „Apache Kafka mit PHP“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser PHP Academy-Lektion Code schreiben und ausführen?
Ja. Jede PHP Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Warum asynchrone Nachrichtenübermittlung
- Mit RabbitMQ in PHP arbeiten
- Apache Kafka mit PHP
- Ereignisgesteuerte Workflows erstellen