Überlegungen zur Nachrichtencodierung
Viele Cloudanwendungen verwenden asynchrone Nachrichten, um Informationen zwischen Komponenten des Systems auszutauschen. Ein wichtiger Aspekt des Messaging ist das Format, das zum Codieren der Nutzlastdaten verwendet wird. Nachdem Sie eine Messagingtechnologieausgewählt haben, besteht der nächste Schritt darin, zu definieren, wie die Nachrichten codiert werden. Es gibt viele Optionen, aber die richtige Wahl hängt von Ihrem Anwendungsfall ab.
In diesem Artikel werden einige der Überlegungen beschrieben.
Anforderungen für den Nachrichtenaustausch
Ein Nachrichtenaustausch zwischen einem Produzenten und einem Verbraucher erfordert:
- Ein Shape oder eine Struktur, die die Nutzlast der Nachricht definiert.
- Ein Codierungsformat, das die Nutzlast darstellt.
- Serialisierungsbibliotheken zum Lesen und Schreiben der codierten Nutzlast.
Der Produzent der Nachricht definiert das Nachrichten-Shape basierend auf der Geschäftslogik und den Informationen, die er an die Verbraucher senden möchte. Um das Shape zu strukturieren, teilen Sie die Informationen in einzelne oder verwandte Themen (Felder) auf. Legen Sie die Merkmale der Werte für diese Felder fest. Sie müssen die folgenden Fragen berücksichtigen.
- Was ist der effizienteste Datentyp?
- Hat die Nutzlast immer bestimmte Felder?
- Verfügt die Nutzlast über einen einzelnen Datensatz oder einen wiederholten Wertesatz?
Wählen Sie dann je nach Bedarf ein Codierungsformat aus. Zu bestimmten Faktoren gehören die Möglichkeit, hochstrukturierte Daten zu erstellen, wenn Sie sie benötigen, Zeit zum Codieren und Übertragen der Nachricht und die Möglichkeit, die Nutzlast zu analysieren. Wählen Sie je nach Codierungsformat eine Serialisierungsbibliothek aus, die gut unterstützt wird.
Ein Verbraucher der Nachricht muss diese Entscheidungen kennen, damit er weiß, wie eingehende Nachrichten gelesen werden.
Um Nachrichten zu übertragen, serialisiert der Produzent die Nachricht in ein Codierungsformat. Am empfangenden Ende deserialisiert der Verbraucher die Nutzlast, um die Daten zu verwenden. Auf diese Weise teilen beide Entitäten das Modell und solange sich die Form nicht ändert, wird die Nachrichtenübermittlung ohne Probleme fortgesetzt. Wenn sich der Vertrag ändert, sollte das Codierungsformat in der Lage sein, die Änderung zu verarbeiten, ohne den Verbraucher zu unterbrechen.
Einige Codierungsformate wie JSON sind selbstbeschreibungen, d. h. sie können analysiert werden, ohne auf ein Schema zu verweisen. Solche Formate neigen jedoch dazu, größere Nachrichten zu liefern. Bei anderen Formaten werden die Daten möglicherweise nicht so einfach analysiert, aber die Nachrichten sind kompakt. In diesem Artikel werden einige Faktoren erläutert, die Ihnen bei der Auswahl eines Formats helfen können.
Überlegungen zum Codierungsformat
Das Codierungsformat definiert, wie eine Gruppe strukturierter Daten als Bytes dargestellt wird. Der Nachrichtentyp kann die Formatauswahl beeinflussen. Nachrichten im Zusammenhang mit Geschäftstransaktionen enthalten höchstwahrscheinlich hochgradig strukturierte Daten. Außerdem können Sie es später für Überwachungszwecke abrufen. Bei einem Datenstrom von Ereignissen sollten Sie eine Abfolge von Datensätzen so schnell wie möglich lesen und für statistische Analysen speichern.
Hier sind einige Punkte, die Sie beim Auswählen eines Codierungsformats berücksichtigen sollten.
Menschliche Lesbarkeit
Die Nachrichtencodierung kann allgemein in textbasierte und binäre Formate unterteilt werden.
Bei textbasierter Codierung befindet sich die Nachrichtennutzlast im Nur-Text-Format und kann daher von einer Person ohne Verwendung von Codebibliotheken geprüft werden. Lesbare Formate sind für Archivdaten geeignet. Da die Nutzlast von einem Menschen gelesen werden kann, lassen sich textbasierte Formate leichter debuggen und für das Troubleshooting an Protokolle senden.
Der Nachteil ist, dass die Nutzlast tendenziell größer ist. Die Nutzlastgröße kann oft durch einen Minimierungsprozess reduziert werden, solange sie bei Bedarf zur menschlichen Lesbarkeit umgekehrt werden kann. Gängige textbasierte Formate sind JSON und YAML.
Verschlüsselung
Wenn vertrauliche Daten in den Nachrichten vorhanden sind, überlegen Sie, ob diese Nachrichten vollständig verschlüsselt werden sollen, wie in dieser Anleitung beschrieben, um Azure Service Bus-Daten im Ruhezustand zu verschlüsseln. Wenn nur bestimmte Felder verschlüsselt werden müssen und Sie die Cloudkosten lieber reduzieren möchten, sollten Sie dazu eine Bibliothek wie NServiceBus verwenden.
Codierungsgröße
Die Nachrichtengröße wirkt sich auf die Netzwerk-E/A-Leistung über das Netzwerk aus. Binärformate sind kompakter als textbasierte Formate. Binäre Formate erfordern Serialisierungs-/Deserialisierungsbibliotheken. Die Nutzlast kann nicht gelesen werden, es sei denn, sie ist dekodiert.
Verwenden Sie ein Binärformat, wenn Sie den Drahtbedarf reduzieren und Nachrichten schneller übertragen möchten. Diese Formatkategorie wird in Szenarien empfohlen, in denen Speicher- oder Netzwerkbandbreite ein Problem darstellt. Optionen für Binäre Formate sind Apache Avro, Google Protocol Buffers (Protobuf), MessagePack und Concise Binary Object Representation (CBOR). Die Vor- und Nachteile dieser Formate werden später in Auswahlmöglichkeiten für Codierungsformatebeschrieben.
Der Nachteil besteht darin, dass die Nutzlast nicht menschenlesbar ist. Die meisten Binärformate verwenden komplexe Systeme, die für die Wartung kostspielig sein können. Außerdem benötigen sie spezielle Bibliotheken zum Decodieren, was möglicherweise nicht unterstützt wird, wenn Sie Archivierungsdaten abrufen möchten.
Bei nichtbinären Formaten kann die Verwendung eines Minimierungsprozesses, um technisch unnötige Leerzeichen und Zeichen zu entfernen und dennoch die Ausrichtung an die Spezifikation des Formats beizubehalten, bei der Codierungsgröße helfen. Bewerten Sie die Funktionen Ihres Encoders, um die Minimierung zur Standardeinstellung zu machen. Beispiel: JsonSerializerOptions.WriteIndented
aus dem System.Text.Json.JsonSerializer
von .NET steuert die automatische Minimierung beim Erstellen von JSON-Text.
Grundlegendes zur Nutzlast
Eine Nachrichtennutzlast wird als Sequenz von Bytes empfangen. Um diese Sequenz zu analysieren, muss der Consumer Zugriff auf Metadaten haben, die die Datenfelder in der Nutzlast beschreiben. Es gibt zwei Hauptansätze zum Speichern und Verteilen von Metadaten:
Markierte Metadaten. In einigen Codierungen, insbesondere JSON, werden Felder mit dem Datentyp und bezeichner im Textkörper der Nachricht markiert. Diese Formate sind selbstbeschreibend, da Sie ohne Verweis auf ein Schema in ein Wörterbuch mit Werten analysiert werden können. Eine Möglichkeit für den Verbraucher, die Felder zu verstehen, besteht darin, nach erwarteten Werten abzufragen. Der Producer sendet beispielsweise eine Nutzlast in JSON. Der Consumer analysiert den JSON-Code in ein Wörterbuch und prüft auf das Vorhandensein Feldern, um die Nutzlast zu verstehen. Eine andere Möglichkeit besteht darin, dass der Verbraucher ein vom Hersteller gemeinsam genutztes Datenmodell anwendet. Wenn Sie beispielsweise eine statisch eingegebene Sprache verwenden, können viele JSON-Serialisierungsbibliotheken eine JSON-Zeichenfolge in eine typierte Klasse analysieren.
Schema. Ein Schema definiert formell die Struktur- und Datenfelder einer Nachricht. In diesem Modell haben der Hersteller und der Verbraucher einen Vertrag über ein gut definiertes Schema. Das Schema kann die Datentypen, erforderlichen/optionalen Felder, Versionsinformationen und die Struktur der Nutzlast definieren. Der Producer sendet die Nutzlast gemäß dem Writer-Schema. Der Consumer empfängt die Nutzlast durch Anwenden eines Reader-Schemas. Die Nachricht wird mithilfe der codierungsspezifischen Bibliotheken serialisiert/deserialisiert. Es gibt zwei Möglichkeiten zum Verteilen von Schemas:
Speichern Sie das Schema als Präambel oder Kopfzeile in der Nachricht, aber getrennt von der Nutzlast.
Speichern Sie das Schema extern.
Einige Codierungsformate definieren das Schema und verwenden Tools, die Klassen aus dem Schema generieren. Der Produzent und Konsument verwenden diese Klassen und Bibliotheken, um die Nutzlast zu serialisieren und zu deserialisieren. Die Bibliotheken bieten außerdem Kompatibilitätsüberprüfungen zwischen dem Schreiber- und Leseschema. Sowohl protobuf als auch Apache Avro folgen diesem Ansatz. Der Hauptunterschied besteht darin, dass protobuf über eine sprachunabhängige Schemadefinition verfügt, aber Avro verwendet kompaktes JSON. Ein weiterer Unterschied besteht darin, dass beide Formate Kompatibilitätsprüfungen zwischen Reader- und Writer-Schemas bereitstellen.
Eine weitere Möglichkeit zum externen Speichern des Schemas in einer Schemaregistrierung. Die Nachricht enthält einen Verweis auf das Schema und die Nutzlast. Der Producer sendet den Schemabezeichner in der Nachricht, und der Consumer ruft das Schema durch Angabe dieses Bezeichners aus einem externen Speicher ab. Beide Parteien verwenden eine formatspezifische Bibliothek zum Lesen und Schreiben von Nachrichten. Neben dem Speichern des Schemas kann eine Registrierung Kompatibilitätsprüfungen bereitstellen, um sicherzustellen, dass der Vertrag zwischen Produzent und Verbraucher nicht unterbrochen wird, während sich das Schema weiterentwickelt.
Bevor Sie einen Ansatz auswählen, entscheiden Sie, was wichtiger ist: die Größe der Übertragungsdaten oder die Möglichkeit, die archivierten Daten später zu analysieren.
Das Speichern des Schemas zusammen mit der Nutzlast führt zu einer größeren Codierungsgröße und wird für intermittierende Nachrichten bevorzugt. Wählen Sie diesen Ansatz aus, wenn das Übertragen kleinerer Byteabschnitte von entscheidender Bedeutung ist oder Sie eine Abfolge von Datensätzen erwarten. Die Kosten für die Wartung eines externen Schemaspeichers können hoch sein.
Wenn jedoch die On-Demand-Decodierung der Nutzlast wichtiger ist als die Größe, garantiert das Einbeziehen des Schemas mit der Nutzlast oder des getaggten Metadatenansatzes die anschließende Decodierung. Es kann eine erhebliche Zunahme der Nachrichtengröße geben und sich auf die Speicherkosten auswirken.
Versionsverwaltung des Schemas
Wenn sich die Geschäftsanforderungen ändern, wird erwartet, dass sich das Shape ändert, und das Schema wird sich weiterentwickeln. Mit der Versionsverwaltung kann der Produzent Schemaupdates angeben, die möglicherweise neue Features enthalten. Es gibt zwei Aspekte für die Versionsverwaltung:
Der Verbraucher sollte sich der Änderungen bewusst sein.
Eine Möglichkeit besteht darin, dass der Verbraucher alle Felder überprüft, um festzustellen, ob sich das Schema geändert hat. Eine weitere Möglichkeit besteht darin, dass der Produzent eine Schemaversionsnummer mit der Nachricht veröffentlicht. Wenn sich das Schema weiterentwickelt, erhöht der Produzent die Version.
Änderungen dürfen die Geschäftslogik von Verbrauchern nicht beeinflussen oder unterbrechen.
Angenommen, ein Feld wird einem vorhandenen Schema hinzugefügt. Wenn Nutzer, die die neue Version verwenden, eine Nutzlast gemäß der alten Version erhalten, könnten ihre Abläufe gestört werden, wenn sie das Fehlen des neuen Felds nicht ignorieren können. Wenn Sie den umgekehrten Fall berücksichtigen, nehmen Sie an, dass ein Feld im neuen Schema entfernt wird. Verbraucher, die das alte Schema verwenden, können die Daten möglicherweise nicht lesen.
Codierungsformate wie Avro bieten die Möglichkeit, Standardwerte zu definieren. Wenn im vorherigen Beispiel das Feld mit einem Standardwert hinzugefügt wird, wird das fehlende Feld mit dem Standardwert aufgefüllt. Andere Formate wie protobuf bieten ähnliche Funktionen über erforderliche und optionale Felder.
Nutzlaststruktur
Überlegen Sie, wie Daten in der Nutzlast angeordnet werden. Handelt es sich um eine Abfolge von Datensätzen oder eine einzelne Nutzlast? Die Nutzlaststruktur kann in einem der folgenden Modelle kategorisiert werden:
Array/Wörterbuch/Wert: Definiert Einträge, die Werte in einem oder mehreren dimensionalen Arrays enthalten. Einträge weisen eindeutige Schlüssel-Wert-Paare auf. Es kann erweitert werden, um die komplexen Strukturen darzustellen. Einige Beispiele sind JSON, Apache Avro und MessagePack.
Dieses Layout eignet sich, wenn Nachrichten individuell mit unterschiedlichen Schemas codiert sind. Wenn Sie über mehrere Datensätze verfügen, kann die Nutzlast übermäßig redundant werden, was dazu führt, dass sie sich aufbläht.
Tabellarische Daten: Informationen werden in Zeilen und Spalten unterteilt. Jede Spalte gibt ein Feld oder den Betreff der Informationen an, und jede Zeile enthält Werte für diese Felder. Dieses Layout ist effizient für einen wiederholten Satz von Informationen, z. B. Zeitreihendaten.
CSV ist eines der einfachsten textbasierten Formate. Es stellt Daten als Abfolge von Datensätzen mit einem allgemeinen Header dar. Bei der binären Codierung hat Apache Avro eine Vorambel, die einem CSV-Header ähnelt, jedoch eine kompakte Codierungskomprimierung erzeugt.
Bibliotheksunterstützung
Sie sollten bekannte Formate anstatt eines proprietären Modells verwenden. Bekannte Formate werden durch Bibliotheken unterstützt, die von der Community universell unterstützt werden. Mit spezialisierten Formaten benötigen Sie bestimmte Bibliotheken. Ihre Geschäftslogik muss möglicherweise einige der api-Designoptionen umgehen, die von den Bibliotheken bereitgestellt werden.
Wählen Sie für schemabasiertes Format eine Codierungsbibliothek aus, die Kompatibilitätsprüfungen zwischen dem Reader- und Writer-Schema vorgibt. Bestimmte Codierungsbibliotheken, z. B. Apache Avro, erwarten, dass der Benutzer sowohl das Schreiberschema als auch das Leseschema angeben muss, bevor die Nachricht deserialisiert wird. Diese Überprüfung stellt sicher, dass der Verbraucher die Schemaversionen kennt.
Interoperabilität
Ihre Auswahl an Formaten hängt möglicherweise von der jeweiligen Arbeitslast oder dem Technologieökosystem ab.
Zum Beispiel:
Azure Stream Analytics verfügt über systemeigene Unterstützung für JSON, CSV und Avro. Wenn Ihre Workload Stream Analytics verwendet, ist es sinnvoll, eines dieser Formate auszuwählen.
JSON ist ein Standardmäßiges Austauschformat für HTTP-REST-APIs. Wenn Ihre Anwendung JSON-Nutzlasten von Clients empfängt und diese dann zur asynchronen Verarbeitung in eine Nachrichtenwarteschlange eingibt, kann es sinnvoll sein, JSON für das Messaging zu verwenden, anstatt sie in einem anderen Format erneut zu codieren.
Dies sind nur zwei Beispiele für Interoperabilitätsüberlegungen. Im Allgemeinen sind standardisierte Formate interoperabler als benutzerdefinierte Formate. In textbasierten Optionen ist JSON einer der interoperabelsten.
Auswahlmöglichkeiten für Codierungsformate
Hier sind einige gängige Codierungsformate. Berücksichtigen Sie die Überlegungen, bevor Sie ein Format auswählen.
JSON
JSON- ist ein offener Standard (IETF RFC8259). Es ist ein textbasiertes Format, das dem Array-/Wörterbuch-/Wertmodell folgt.
JSON kann zum Kategorisieren von Metadaten verwendet werden und Sie können die Nutzlast ohne Schema analysieren. JSON unterstützt die Option zum Angeben optionaler Felder, die bei der Vorwärts- und Abwärtskompatibilität hilfreich sind.
Der größte Vorteil besteht darin, dass es universell verfügbar ist. Es ist am interoperabelsten und das Standardcodierungsformat für viele Messagingdienste.
Als textbasiertes Format ist es nicht effizient über das Kabel und nicht eine ideale Wahl in Fällen, in denen die Speicherung ein Problem darstellt. Verwenden Sie möglichst Minifizierungstechniken. Wenn Sie zwischengespeicherte Elemente direkt über HTTP an einen Client zurückgeben, kann die Speicherung von JSON die Kosten der Deserialisierung aus einem anderen Format und der anschließenden Serialisierung in JSON sparen.
Verwenden Sie JSON für Einzeldatensatznachrichten oder für eine Abfolge von Nachrichten, in denen jede Nachricht ein anderes Schema aufweist. Vermeiden Sie JSON für eine Abfolge von Datensätzen, z. B. für Daten aus Zeitreihen.
Es gibt andere Variationen von JSON wie binäres JSON (BSON), das eine binäre Codierung ist, die darauf ausgerichtet ist, mit MongoDB zu arbeiten.
Durch Trennzeichen getrennte Werte (CSV)
CSV ist ein textbasiertes tabellarisches Format. Die Kopfzeile der Tabelle gibt die Felder an. Dies ist eine bevorzugte Wahl, bei der die Nachricht eine Reihe von Datensätzen enthält.
Der Nachteil ist mangelnde Standardisierung. Es gibt viele Möglichkeiten zum Ausdrücken von Trennzeichen, Kopfzeilen und leeren Feldern.
Protokollpuffer (Protobuf)
Protokollpuffer (oder Protobuf) ist ein Serialisierungsformat, das stark typierte Definitionsdateien verwendet, um Schemas in Schlüssel-Wert-Paaren zu definieren. Diese Definitionsdateien werden dann zu sprachspezifischen Klassen kompiliert, die zum Serialisieren und Deserialisieren von Nachrichten verwendet werden.
Die Nachricht enthält eine komprimierte binäre kleine Nutzlast, was zu einer schnelleren Übertragung führt. Der Nachteil ist, dass die Nutzlast nicht menschlich lesbar ist. Da das Schema extern ist, empfiehlt es sich auch nicht für Fälle, in denen Archivierte Daten abgerufen werden müssen.
Apache Avro
Apache Avro ist ein binäres Serialisierungsformat, das definitionsdatei ähnlich wie protobuf verwendet, aber es gibt keinen Kompilierungsschritt. Stattdessen enthalten serialisierte Daten immer eine Schema-Präambel.
Die Präambel kann die Kopfzeile oder einen Schemabezeichner enthalten. Aufgrund der kleineren Codierungsgröße wird Avro für Streamingdaten empfohlen. Da sie auch über eine Kopfzeile verfügt, die für eine Gruppe von Datensätzen gilt, ist es eine gute Wahl für tabellarische Daten.
Apache-Parkett
Apache Parquet ist ein Spalten-Speicherdateiformat, das in der Regel mit Apache Hadoop und verwandten Datenverarbeitungsframeworks verwendet wird.
Das Format unterstützt die Datenkomprimierung und verfügt über einige eingeschränkte Funktionen zur Unterstützung der Schemaentwicklung. Dieses Format ist ein Beispiel für eines, das Sie wahrscheinlich nur aufgrund anderer Big-Data-Technologieoptionen in Ihrer Workload nutzen würden, die für das Erstellen oder Verarbeiten dieser Daten verantwortlich sind.
MessagePack
MessagePack ist ein binäres Serialisierungsformat, das für die Übertragung über den Draht kompakt ist. Es gibt keine Nachrichtenschemas oder Nachrichtentypüberprüfungen. Dieses Format wird für den Massenspeicher nicht empfohlen.
CBOR
Concise Binary Object Representation (CBOR) (Specification) ist ein Binärformat, das eine geringe Codierungsgröße bietet. Der Vorteil von CBOR over MessagePack ist, dass es mit IETF in RFC7049 kompatibel ist.