- JSON, das sich als Standard für Web-APIs etabliert hat, ist leicht lesbar und flexibel, hat aber Grenzen bei Leistung und Stabilität
- Protobuf (Protocol Buffers) sorgt mit strikter Typdefinition und automatischer Codegenerierung für klar abgesicherte Datenstrukturen
- Durch binäre Serialisierung werden im Vergleich zu JSON die Datengröße um mehr als das Dreifache reduziert und die Übertragungsgeschwindigkeit verbessert
- Da Server und Client dasselbe
.proto-Schema teilen, sind Typabweichungen oder manuelle Validierung nicht nötig - Debugging ist schwieriger, doch in Bezug auf Leistung, Wartbarkeit und Entwicklungseffizienz ist Protobuf für moderne APIs besser geeignet
Die Verbreitung und Grenzen von JSON
- JSON ist ein für Menschen gut lesbares Textformat, bei dem sich Daten schon mit einfachem
console.log()prüfen lassen - Dank der nahtlosen Integration ins Web wird es in JavaScript und in Backend-Frameworks allgemein breit eingesetzt
- Es bietet Flexibilität, weil Felder frei hinzugefügt, entfernt oder im Typ geändert werden können, was allerdings auch zu Strukturabweichungen oder Fehlern führen kann
- Das Tooling-Ökosystem ist reichhaltig, sodass sich JSON schon mit einem Texteditor oder
curlleicht handhaben lässt - Trotz dieser Vorteile gibt es bei Performance und Typsicherheit bessere Alternativen
Überblick über Protobuf
- Ein binäres Serialisierungsformat, das Google 2001 entwickelt und 2008 veröffentlicht hat
- Wird breit in internen Systemen und für die Kommunikation zwischen Microservices verwendet
- Oft besteht das Missverständnis, dass es nur zusammen mit gRPC nutzbar ist, dabei kann Protobuf auch eigenständig in HTTP-APIs eingesetzt werden
- Anfangs war der Zugang wegen der fehlenden Sichtbarkeit des Binärformats schwieriger, dafür liegen seine Stärken klar bei Effizienz und Stabilität
Starkes Typsystem und Codegenerierung
- Protobuf definiert über
.proto-Dateien Datenstrukturen eindeutig- Jedes Feld hat einen strikten Typ, eine numerische Kennung und einen festen Namen
- Beispiel:
message User { int32 id = 1; string name = 2; string email = 3; bool isActive = 4; } - Der Befehl
protocunterstützt die automatische Codegenerierung für viele Sprachen wie Dart, TypeScript, Kotlin, Swift, C#, Go und Rust - Mit dem generierten Code werden Serialisierung (
writeToBuffer) und Deserialisierung (fromBuffer) ausgeführt, ohne manuelle Validierung oder Parsing - Das spart letztlich Zeit und verbessert zugleich die Wartbarkeit
Effizienz binärer Serialisierung
- Protobuf serialisiert nicht als Text, sondern als Binärdaten und ist dadurch sehr kompakt und schnell
- Größenvergleich derselben Daten (
User-Objekt):- JSON: 86 Byte (68 Byte ohne Leerzeichen)
- Protobuf: 30 Byte
- Gründe für die Effizienz:
- Varint-Encoding für Zahlen
- numerische Tags statt Textschlüsseln
- keine Leerzeichen und keine unnötige Syntax
- Optimierung optionaler Felder
- Das führt zu geringerem Bandbreitenverbrauch, schnelleren Antwortzeiten, weniger mobilem Datenverbrauch und einer besseren User Experience
Beispiel einer Protobuf-API auf Basis von Dart
- Mit dem Paket
shelflässt sich ein einfacher HTTP-Server aufsetzen, der einUser-Objekt als Protobuf zurückgibt - Wichtige Punkte im Server-Code:
- Ein
User()-Objekt erzeugen und mitwriteToBuffer()serialisieren - Im Response-Header
'content-type': 'application/protobuf'setzen
- Ein
- Der Client verwendet das Paket
httpunduser.pb.dart, um Protobuf-Daten direkt zu dekodieren - Da Server und Client dasselbe
.proto-Schema teilen, entstehen keine Abweichungen in der Datenstruktur - Dasselbe Vorgehen lässt sich genauso in Go, Rust, Kotlin, Swift, C#, TypeScript usw. anwenden
Verbleibende Vorteile von JSON
- Bei Protobuf ist es ohne Schema schwer, die Bedeutung zu verstehen
- Statt Feldnamen werden nur numerische Kennungen angezeigt, was für Menschen schwer lesbar ist
- Beispielvergleich:
- JSON:
{ "id": 42, "name": "Alice" } - Protobuf:
1: 42, 2: "Alice"
- JSON:
- Deshalb braucht Protobuf:
- spezielle Decoding-Tools
- Schema- und Versionsmanagement
- Trotzdem sind die Vorteile bei Leistung und Effizienz deutlich größer
Fazit
- Protobuf ist eine ausgereifte und leistungsstarke Serialisierungstechnologie, die auch in öffentlichen APIs gut einsetzbar ist
- Funktioniert auch ohne gRPC eigenständig in einer gewöhnlichen HTTP-API
- Ein Werkzeug, das Leistung, Robustheit, Fehlerreduktion und Entwicklungseffizienz zugleich verbessert
- Für Projekte der nächsten Generation lohnt sich die Einführung von Protobuf auf jeden Fall
10 Kommentare
TypeSpec
https://typespec.io/
Wie wäre es damit? https://msgpack.org/
MessagePack ist auch gut.
Ich halte es für widersprüchlich, ein Format als ausgereift zu bezeichnen, für das es nicht einmal einen offiziellen Decoder zum Debuggen gibt.
Wie bei allen Tools gibt es auch hier keine Universallösung, aber ich halte Protobuf trotzdem für ein durchaus gutes Werkzeug.
Insbesondere gab es einmal den Fall, dass wir in einer Embedded-Umgebung große Datenmengen mit hoher Frequenz (20-mal pro Sekunde) an verschiedene Sprachen/Clients senden mussten, und das haben wir mit nanopb sauber umgesetzt.
Wenn man es so streng handhabt, kommt dann nicht am Ende XML dabei heraus? Haha
Wenn man das Schema auch per DTD definiert und auf der Parser-Seite zwischenspeichert, hätte das zudem den Effekt, dass das Schema nur einmal übertragen werden muss.
=> Muss das Schema nicht ohnehin zwingend mindestens einmal übertragen werden? Auch bei JSON gibt es nicht wirklich kein Schema; es ist implizit in den Daten enthalten. Daher scheint es mir nicht so zu sein, dass kein Schema übertragen wird. Im Gegenteil: Da das Schema für jedes einzelne Feld redundant mitübertragen wird, ist es sogar noch ineffizienter. Eine „schema-basierte Form, die zugleich das Schema in der Nachricht enthält“, klingt eigentlich ziemlich gut.
Hacker-News-Kommentare
JSON führt oft zu mehrdeutigen oder nicht garantierten Daten. Es kommt zu fehlenden Feldern, Typfehlern, Tippfehlern bei Schlüsseln oder undokumentierten Strukturen. Es gab jedoch einen Beitrag, der behauptete, dass so etwas mit Protobuf unmöglich sei, weil die Nachrichtenstruktur durch
.proto-Dateien klar definiert werde. Das missversteht jedoch die Philosophie von Protobuf. Inproto3werden required-Felder überhaupt nicht unterstützt. Auch die offizielle Dokumentation (Protobuf Best Practices) sagt ausdrücklich, dass „required-Felder schädlich waren und entfernt wurden“. Letztlich müssen auch Protobuf-Clients defensiv geschrieben werden, genau wie JSON-APIsjson:"-"zu blockieren. Mein Projekt ist unter Gooey zu sehenKomprimiertes JSON ist absolut brauchbar und hat geringe anfängliche Kommunikationskosten. Natürlich gibt es Probleme, wenn Felder fehlen oder Typen geändert werden, aber die meisten scheitern daran, perfekt typisierte Strukturen zu entwerfen und Prozesse zur Versionssynchronisierung aufzubauen. Am Ende gewinnt die Variante mit den geringeren menschlichen Kosten. Deshalb wird JSON nicht verschwinden, bis es eine Alternative mit noch geringeren menschlichen Kommunikationskosten gibt
console.log()debuggen lässt, wird JSON nicht ersetzt werdenProtobuf ist nicht perfekt. Wenn Server und Client zu unterschiedlichen Zeitpunkten ausgerollt werden und unterschiedliche Spezifikationsversionen haben, geht die Sicherheit verloren. Das lässt sich durch Dinge wie kein ID-Reuse und Unknown-Field-Kopien abmildern, aber verteilte Systeme sind grundsätzlich komplex. Trotzdem hat
protobuf3viele Probleme vonprotobuf2gelöst. Früher konnte man nicht unterscheiden, ob ein Standardwert gesetzt oder ein Feld weggelassen wurde, aber heute lässt sich das mit dem TypmessagelösenIm Artikel war von „ultraeffizient“ die Rede, aber gzip wurde nicht erwähnt. Die meisten Textdaten werden bereits automatisch komprimiert übertragen. Deshalb sollte man Protobuf mit gzip-komprimiertem JSON vergleichen
Es ist gut, für bessere Protokolle zu werben, aber Protobuf ersetzt JSON weder bei Effizienz noch bei der Nutzbarkeit vollständig. Wegen seines strikten Schemas verfehlt Protobuf Bereiche, in denen JSON stark ist. Eigentlich wäre CBOR besser als JSON-Ersatz geeignet. CBOR ist so flexibel wie JSON, hat aber eine kompaktere Kodierung
ASN.1 aus dem Jahr 1984 kann bereits flexibler leisten, was Protobuf tut. Mit DER-Kodierung ist es gar nicht so schlecht. Man kann sich das ASN.1-DER-Beispiel ansehen. Protobuf ist zu komplex für das, was es erreicht
Ich habe ein komplettes Produktionssystem mit Protobuf aufgebaut, und der Betrieb war schmerzhaft. Technisch wirkt es gut, aber in der Praxis ist JSON viel einfacher
Protobuf ist großartig, aber schade, dass es kein Zero-Copy unterstützt. Formate wie Cap’n Proto beseitigen Engpässe bei Serialisierung und Deserialisierung
Ich habe in einem NodeJS-Projekt die gesamte API in
.protodefiniert und einen Server gebaut, der je nach Content-Type mit Proto oder JSON antwortet. Das ist viel strukturierter als Swagger. Schade nur, dass Google so etwas nicht als offizielle Bibliothek bereitgestellt hat. gRPC ist wegen der HTTP/2-Abhängigkeit unpraktisch. Nebenbei gesagt halte ich Text proto für die beste Sprache für statische KonfigurationMein Traum-Binärformat wäre schema-basiert, würde das Schema aber in der Nachricht selbst mitführen. Dann könnte man es direkt mit einem Vim-Plugin lesen. Wenn man mit Millionen Objekten arbeitet, ist es kein großes Problem, ein 1-KB-Schema an eine 2-GB-Nachricht anzuhängen
„Debugging ist zwar schwierig“
Ausgeschieden