Best Practices für das Skalieren von Kafka-basierten Arbeitslasten

Apache Kafka ist bekannt für seine Fähigkeit, eine enorme Menge an Ereignissen in Echtzeit zu verarbeiten. Um jedoch Millionen von Ereignissen zu bewältigen, müssen wir bestimmte Best Practices bei der Implementierung sowohl von Kafka-Producer- als auch von Consumer-Services befolgen.

Bevor Sie Kafka in Ihren Projekten verwenden, lassen Sie uns verstehen, wann man Kafka einsetzen sollte:

  • Hochvolumige EreignisströmeWenn Ihre Anwendung/Ihr Service einen kontinuierlichen Strom von Ereignissen erzeugt, wie z. B. Benutzeraktivitätsereignisse, Website-Klickereignisse, Sensordatenereignisse, Protokollereignisse oder Börsenaktualisierungen, ist die Fähigkeit von Kafka, große Volumina mit niedriger Latenz zu verarbeiten, äußerst nützlich.
  • Echtzeitanalysen. Kafka ist besonders sehr hilfreich beim Aufbau von Echtzeit-Datenverarbeitungs-Pipelines, in denen Daten verarbeitet werden müssen, sobald sie eintreffen. Es ermöglicht Ihnen, Daten an Analyse-Engines wie Kafka Streams, Apache Spark oder Flink für sofortige Analysen/Einblicke und Streaming- oder Batch-Verarbeitung zu streamen.
  • Entkopplung von Anwendungen. Während es als zentrales Nachrichten-Hub fungiert, kann es verschiedene Teile einer Anwendung entkoppeln, was eine unabhängige Entwicklung und Skalierung von Services ermöglicht und das Prinzip der verantwortungsvollen Trennung fördert.
  • Datenintegration über SystemeBei der Integration verteilter Systeme kann Kafka Daten effizient zwischen verschiedenen Anwendungen über Teams/Projekte hinweg übertragen und fungiert als zuverlässiger Datenbroker.

Wichtige Unterschiede zu anderen Warteschersystemen 

Im Folgenden sind die Unterschiede von Apache Kafka zu Systemen wie ActiveMQ, ZeroMQ und VerneMQ aufgeführt:

Persistente Speicherung

Kafka speichert Ereignisse in einem verteilten Protokoll, was die Möglichkeit bietet, Daten jederzeit wiederzugeben, sowie Datenpersistenz selbst im Falle von System-/Knotenfehlern, im Gegensatz zu einigen traditionellen Nachrichtenwarteschlangen, die möglicherweise auf In-Memory-Speicher wie Redis angewiesen sind.

Partitionierung

Daten werden über Broker/Themen partitioniert, was die parallele Verarbeitung großer Datenströme und hohe Durchsatzraten ermöglicht. Dies hilft Verbraucherdatenströmen, sich mit einzelnen Partitionen zu verbinden und fördert die horizontale Skalierbarkeit.

Verbrauchergruppen

Mehrere Verbraucher können sich für dasselbe Thema anmelden und von verschiedenen Offsets innerhalb einer Partition lesen, was doppelte Verbrauchsmuster ermöglicht, damit verschiedene Teams dieselben Daten für unterschiedliche Zwecke konsumieren und verarbeiten können. Einige Beispiele sind:

  • Benutzeraktivitäten, die von ML-Teams konsumiert werden, um verdächtige Aktivitäten zu erkennen
  • Empfehlungsteam zur Erstellung von Empfehlungen
  • Werteam zur Generierung relevanter Anzeigen

Best Practices für Kafka Producer

Batchgröße und Linger-Zeit

Durch die Konfiguration von batch.size und linger.ms können Sie den Durchsatz Ihres Kafka Producers erhöhen. batch.size ist die maximale Größe des Batches in Bytes. Kafka wird versuchen, es zu bündeln, bevor es an die Producer gesendet wird. 

Linger.ms bestimmt die maximale Zeit in Millisekunden, die der Producer warten wird, um zusätzliche Nachrichten zum Batch hinzuzufügen, bevor er mit der Verarbeitung beginnt. 

Die Konfiguration der Einstellungen batch size und linger.ms trägt erheblich zur Leistung des Systems bei, indem sie steuert, wie viele Daten angesammelt werden, bevor sie an die Verarbeitungssysteme gesendet werden, was eine bessere Durchsatzrate und reduzierte Latenzen beim Umgang mit großen Datenmengen ermöglicht. Es kann auch leichte Verzögerungen einführen, abhängig von den gewählten Werten. Insbesondere kann eine große Batchgröße mit einem korrekt eingestellten linger.ms die Effizienz des Datentransfers optimieren.

Kompression

Eine weitere Möglichkeit, den Durchsatz zu erhöhen, besteht darin, die Kompression über die Konfiguration compression.type zu aktivieren. Der Produzent kann Daten mit gzip, snappy oder lz4 komprimieren, bevor er sie an die Broker sendet. Bei großen Datenmengen hilft diese Konfiguration, den Kompressionsaufwand mit der Netzwerk-Effizienz zu optimieren. Sie spart auch Bandbreite und erhöht den Durchsatz des Systems. Darüber hinaus können wir durch das Setzen des geeigneten Serializers und Key-Serializers sicherstellen, dass die Daten in einem Format serialisiert werden, das mit Ihren Konsumenten kompatibel ist.

Wiederholungen und Idempotenz

Um die Zuverlässigkeit des Kafka-Produzenten zu gewährleisten, sollten Sie Wiederholungen und Idempotenz aktivieren. Durch die Konfiguration von retries kann der Produzent automatisch jede Datencharge erneut senden, die vom Broker innerhalb einer bestimmten Anzahl von Versuchen kein ack erhält.

Bestätigungen

Diese Konfiguration steuert das Maß an Bestätigung, das vom Broker erforderlich ist, bevor eine Nachricht als erfolgreich gesendet betrachtet wird. Durch die Wahl des richtigen acks-Niveaus können Sie die Zuverlässigkeit Ihrer Anwendung steuern. Im Folgenden sind die akzeptierten Werte für diese Konfiguration aufgeführt.

  • – am schnellsten, aber keine Garantie für die Zustellung der Nachricht.
  • 1 – die Nachricht wird bestätigt, sobald sie an den führenden Broker geschrieben wurde, was grundlegende Zuverlässigkeit bietet.
  • all – die Nachricht wird nur dann als zugestellt betrachtet, wenn alle Replikate sie bestätigt haben, was hohe Haltbarkeit gewährleistet.

Konfigurationsanpassung basierend auf der Arbeitslast

Sie sollten beginnen, Metriken wie die Nachrichtenversandrate, die Batch-Größe und die Fehlerquoten zu verfolgen, um Leistungsengpässe zu identifizieren. Überprüfen und passen Sie regelmäßig die Einstellungen des Produzenten basierend auf den Änderungen oder Aktualisierungen der Funktionen/Daten an.

Kafka Verbraucher Best Practices

Verbrauchergruppen

Jeder Kafka Verbraucher sollte zu einer Verbrauchergruppe gehören; eine Verbrauchergruppe kann einen oder mehrere Verbraucher enthalten. Durch die Erstellung weiterer Verbraucher in der Gruppe können Sie die Leseleistung von allen Partitionen skalieren, was es Ihnen ermöglicht, ein riesiges Datenvolumen zu verarbeiten. Die group.id Konfiguration hilft, die Verbrauchergruppe zu identifizieren, zu der der Verbraucher gehört, was eine Lastverteilung über mehrere Verbraucher ermöglicht, die vom gleichen Thema konsumieren. Die beste Praxis ist es, sinnvolle Gruppen-IDs zu verwenden, um Verbrauchergruppen innerhalb Ihrer Anwendung leicht zu identifizieren. 

Offset-Commitment

Sie können steuern, wann Ihre Anwendung Offsets committen soll, was helfen kann, Datenverlust zu vermeiden. Es gibt zwei Möglichkeiten, Offsets zu committen: automatisch und manuell. Für hochvolumige Anwendungen sollten Sie das manuelle Commit für eine bessere Kontrolle in Betracht ziehen.

  • auto.offset.reset – legt fest, was zu tun ist, wenn ein Verbraucher ein Thema ohne bestätigte Offsets konsumiert (z.B. ein neues Thema oder ein Verbraucher, der zum ersten Mal einer Gruppe beitritt). Optionen sind earliest (vom Anfang lesen), latest (vom Ende lesen) oder none (einen Fehler werfen). Wählen Sie „earliest“ für die meisten Anwendungsfälle, um Datenverlust zu vermeiden, wenn ein neuer Verbraucher einer Gruppe beitritt.Steuerung, wie ein Verbraucher mit dem Konsumieren von Daten beginnt, um ein korrektes Verhalten sicherzustellen, wenn ein Verbraucher neu gestartet oder einer Gruppe hinzugefügt wird.
  • enable.auto.commit – hilft bei der Konfiguration, um Offsets regelmäßig automatisch zu bestätigen. Allgemein setzen wir den Wert in den meisten Produktionsumgebungen auf false, wenn wir keine hohe Zuverlässigkeit benötigen und bestätigen Offsets manuell innerhalb Ihrer Anwendungslogik, um eine exakte Verarbeitung sicherzustellen.Bietet Kontrolle über Offset-Bestätigungen, um eine präzisere Steuerung der Datenverarbeitung zu ermöglichen.
  • auto.commit.interval.ms intervall in Millisekunden, in dem Offsets automatisch übernommen werden, wenn enable.auto.commit auf true gesetzt ist. Ändern Sie basierend auf der Verarbeitungszeit Ihrer Anwendung, um Datenverlust aufgrund unerwarteter Fehler zu vermeiden.

Fetch-Größe und Maximalanzahl an Poll-Datensätzen

Diese Konfiguration hilft, die Anzahl der Datensätze zu steuern, die in jeder Anfrage abgerufen werden. Konfigurieren Sie fetch.min.bytes und max.poll.records. Eine Erhöhung dieses Wertes kann dazu beitragen, den Durchsatz Ihrer Anwendungen zu verbessern, während die CPU-Auslastung reduziert und die Anzahl der an Broker gesendeten Aufrufe verringert wird.

  • fetch.min.bytes – die minimale Anzahl von Bytes, die in einer einzelnen Poll-Anfrage von einem Broker abgerufen werden sollen. Setzen Sie einen kleinen Wert, um unnötige Netzwerkaufrufe zu vermeiden, aber nicht zu klein, um übermäßiges Polling zu verhindern. Es hilft, die Netzwerkeffizienz zu optimieren, indem kleine, häufige Anfragen verhindert werden.
  • fetch.max.bytes die maximale Anzahl von Bytes, die in einer einzigen Abfrage von einem Broker abgerufen werden sollen. Passt basierend auf dem verfügbaren Speicher an, um eine Überlastung der Verbraucherarbeiter zu vermeiden. Dies reduziert die Menge an Daten, die in einer einzigen Abfrage abgerufen wird, und vermeidet Speicherprobleme.
  • max.poll.interval.ms – die maximale Zeit, die gewartet werden soll, bis eine Abfrage Daten zurückgibt, bevor sie abläuft. Setzen Sie einen guten Timeout, um zu vermeiden, dass Verbraucher hängen bleiben oder Verzögerungen auftreten, wenn keine Daten verfügbar sind. Es hilft zu verhindern, dass Verbraucher zu lange auf Nachrichten warten. (Manchmal können k8s-Pods neu gestartet werden, wenn die Liveness-Proben betroffen sind).

Partitionszuweisung

Dies ist die Strategie, die verwendet wird, um Partitionen (partition.assignment.strategy) Verbrauchern innerhalb einer Gruppe zuzuweisen (z. B. range, roundrobin). Verwenden Sie range für die meisten Szenarien, um Partitionen gleichmäßig auf Verbraucher zu verteilen. Dies ermöglicht eine ausgewogene Lastverteilung unter den Verbrauchern in einer Gruppe.

Hier sind einige wichtige Überlegungen, bevor Sie Kafka verwenden:

  • KomplexitätDie Implementierung von Kafka erfordert ein tieferes Verständnis der Konzepte verteilter Systeme wie Partitionierung und Offset-Management aufgrund seiner fortschrittlichen Funktionen und Konfigurationen.
  • Überwachung und Verwaltung. Die Implementierung von Überwachung und Verwaltung des Kafka-Clusters ist wichtig, um hohe Verfügbarkeit und Leistung sicherzustellen.
  • SicherheitDie Implementierung robuster Sicherheitspraktiken zum Schutz sensibler Daten, die durch die Kafka-Themen fließen, ist ebenfalls wichtig.

Die Umsetzung dieser Best Practices kann Ihnen helfen, Ihre auf Kafka basierenden Anwendungen so zu skalieren, dass sie Millionen oder Milliarden von Ereignissen verarbeiten können. Es ist jedoch wichtig, sich daran zu erinnern, dass die optimale Konfiguration je nach den spezifischen Anforderungen Ihrer Anwendung variieren kann.

Source:
https://dzone.com/articles/best-practices-for-scaling-kafka-based-workloads