Protobuf: Strukturierter Code mit Protocol Buffers

Die Strukturierung von Daten spielt bei der Entwicklung von Programmen und Websites eine wichtige Rolle. Sind die Daten eines Projekts nämlich gut strukturiert, lässt es sich von anderer Software gut und präzise auslesen. Im Web ist das vor allem wichtig für die textbasierten Suchmaschinen wie Google, Bing oder Yahoo, die den Inhalt einer Website dank entsprechender, strukturierter Auszeichnungen optimal erfassen können.

Generell lohnt sich der Einsatz von strukturierten Daten in der Softwareentwicklung – ob für Web- oder Desktop-Anwendungen – überall dort, wo Programme bzw. Dienste Daten über Schnittstellen austauschen müssen und eine hohe Datenverarbeitungsgeschwindigkeit gewünscht ist. Welche Rolle dabei das Serialisierungsformat Protocol Buffers (Protobuf) spielen kann und wie sich diese Strukturierungsmethode von der bekannten Alternative JSONP unterscheidet, erfahren Sie in diesem Artikel.

Was ist Protobuf (Protocol Buffers)?

Seit 2008 bietet Google mit Protocol Buffers, kurz Protobuf, ein ursprünglich für die interne Verwendung entwickeltes Datenaustauschformat als Open-Source-Projekt (teilweise Apache-2.0-Lizenz) der breiten Öffentlichkeit an. Das binäre Format ermöglicht Anwendungen die Speicherung sowie den unkomplizierten Austausch strukturierter Daten, wobei diese Programme sogar in verschiedenen Programmiersprachen geschrieben sein können. Zu den unterstützten Sprachen zählen unter anderem folgende:

  • C#
  • C++
  • Go
  • Objective-C
  • Java
  • Python
  • Ruby

Protobuf wird unter anderem in Kombination mit HTTP und RPCs (Remote Procedure Calls) für die lokale und die entfernte Client-Server-Kommunikation verwendet – insbesondere zur Beschreibung der hierfür benötigten Schnittstellen. Die Protokollzusammensetzung wird auch unter der Bezeichnung gRPC zusammengefasst.

Zur Anzeige dieses Videos sind Cookies von Drittanbietern erforderlich. Ihre Cookie-Einstellungen können Sie hier aufrufen und ändern.

Welche Vorteile bietet Google Protocol Buffers?

Bei der Entwicklung von Protobuf hat Google insbesondere auf zwei Faktoren Wert gelegt: Simplizität und Performance. Zum damaligen Entwicklungszeitpunkt sollte das Format – wie bereits erwähnt zunächst unternehmensintern – das ähnlich geartete XML-Format ablösen. Heute steht es zudem in Konkurrenz zu anderen Lösungen wie JSON(P) oder FlatBuffers. Dass Protocol Buffers dennoch für viele Projekte die bessere Wahl ist, macht eine Analyse der Merkmale und Stärken dieser Strukturierungsmethode deutlich:

Übersichtliche, anwendungsübergreifende Schemata

Die Basis jeder erfolgreichen Anwendung ist ein gut organisiertes Datenbanksystem. Der Organisation dieses Systems inklusive der enthaltenen Daten wird häufig eine hohe Beachtung geschenkt – doch spätestens, wenn die Daten an einen fremden Dienst weitergeleitet werden, gehen die zugrundeliegenden Strukturen verloren. Durch die einmalige Kodierung der Daten im Protocol-Buffers-Schema lässt sich sicherstellen, dass Ihr Projekt strukturierte Daten wie gewünscht weiterleitet, ohne dass diese Strukturen aufgebrochen werden.

Rückwärts- und Vorwärtskompatibilität

Die Implementierung von Protobuf erübrigt die lästige Durchführung von Versionsüberprüfungen, die in der Regel mit „ugly“ Code (dt. hässlicher, unsauberer Code) verbunden ist. Um die Abwärtskompatibilität zu älteren Versionen bzw. Vorwärtskompatibilität zu neuen Versionen aufrechterhalten zu können, greift Protocol Buffers auf nummerierte Felder zurück, die zugreifenden Services als Bezugspunkte dienen. Um neue Features und Funktionen zu veröffentlichen, müssen Sie also nicht immer unbedingt den kompletten Codes anpassen.

Flexibilität und Komfort

Bei der Protobuf-Codierung greifen Sie automatisch auf Modifikatoren (wahlweise: vorausgesetzt, optional oder sich wiederholend) zurück, die Ihnen die Programmierarbeit erheblich vereinfachen. So ermöglicht die Strukturierungsmethode, dass Sie die Form Ihrer Datenstruktur auf Schemaebene bestimmen, woraufhin die Implementierungsdetails der verwendeten Klassen für die verschiedenen Programmiersprachen automatisch geregelt werden. Zudem können Sie den Status jederzeit anpassen, beispielsweise von „vorausgesetzt“ auf „optional“. Auch der Transport der Datenstrukturen lässt sich durch Protocol Buffers regulieren: Durch die Kodierung generischer Anfrage- und Antwortstrukturen ist ein flexibler und sicherer Datentransfer zwischen mehreren Diensten auf einfache Weise gewährleistet.

Weniger Boilerplate-Code

Je nach Typ und Komplexität eines Projekts spielt Boilerplate-Code (oder einfach nur Boilerplate) eine mehr oder weniger entscheidende Rolle bei der Programmierung. Vereinfacht gesagt handelt es sich dabei um wiederverwendbare Code-Bausteine, die an vielen Stellen einer Software benötigt werden und für gewöhnlich nur geringfügig anpassbar sind. Häufig dient solcher Code beispielsweise dazu, die Nutzung von Funktionen aus Bibliotheken vorzubereiten. Insbesondere in den Websprachen JavaScript, PHP, HTML und CSS sind Boilerplates verbreitet, obgleich das nicht optimal für die Performance der Webanwendung ist. Ein passendes Procotol-Buffers-Schema hilft dabei, den Boilerplate-Code zu reduzieren und somit die Leistung langfristig zu verbessern.

Einfache Sprachinteroperabilität

Es zählt zum heutigen Standard, dass Anwendungen nicht einfach nur mehr in einer Sprache geschrieben sind, sondern Programmteile bzw. -module verschiedenster Sprachtypen vereinen. Protobuf vereinfacht das Zusammenspiel der einzelnen Code-Bestandteile erheblich: Werden neue Komponenten hinzugefügt, deren Sprache von der aktuellen Projektsprache abweicht, lassen Sie das Protocol-Buffers-Schema einfach mithilfe des passenden Code-Generators in die jeweilige Zielsprache übersetzen, wodurch der eigene Aufwand auf ein Minimum reduziert wird. Voraussetzung ist natürlich, dass es sich bei den verwendeten Sprachen um solche handelt, die von Protobuf unterstützt werden – standardmäßig, wie die bereits aufgezählten Sprachen, oder per Drittanbieter-Add-on.

Protobuf vs. JSON: Die beiden Formate im Vergleich

In erster Linie hat Google Protocol Buffers als Alternative zu XML (Extensible Markup Language) entwickelt und die Auszeichnungssprache in vielerlei Hinsicht auch übertroffen. So ist die Strukturierung der Daten mit Protobuf tendenziell nicht nur einfacher, sondern sorgt laut den Angaben des Suchmaschinenriesens auch für eine Datenstruktur, die zwischen drei bis zehn Mal kleiner und 20 bis 100 Mal schneller als eine vergleichbare XML-Struktur ist.

Auch mit der JavaScript-Auszeichnungssprache JSON (JavaScript Object Notation) tritt Protocol Buffers häufig in den direkten Vergleich, wobei aber zu erwähnen ist, dass beide Technologien mit unterschiedlichen Zielsetzungen designt wurden: JSON ist ein Nachrichtenformat, das aus JavaScript hervorgegangen ist, seine Nachrichten im Textformat austauscht und von praktisch allen gängigen Programmiersprachen unterstützt wird. Der Funktionsumfang von Protobuf umfasst mehr als ein Nachrichtenformat, denn die Google-Technik bietet zusätzlich auch diverse Regeln und Werkzeuge, um die Nachrichten zu definieren und auszutauschen. Grundsätzlich sticht Protobuf JSON auch in Sachen Performance aus, wenn man den Nachrichtenversand im Generellen betrachtet, doch die folgende tabellarische „Protobuf vs. JSON“-Auflistung zeigt, dass beide Strukturierungstechniken ihre Vor- und Nachteile haben:

  Protobuf JSON
Entwickler Google Douglas Crockford
Funktion Auszeichnungsformat für strukturierte Daten (Speicherung und Übertragung) und Bibliothek Auszeichnungsformat für strukturierte Daten (Speicherung und Übertragung)
Binäres Format ja nein
Standardisierung nein ja
Menschenlesbarkeit teilweise ja
Community/Dokumentation kleine Community, ausbaufähige Online-Manuals riesige Community, gut offizielle Dokumentation sowie diverse Online-Tutorials etc.

Wer also ein gut dokumentiertes Serialisierungsformat benötigt, das die strukturierten Daten in für Menschen lesbarer Form speichert und überträgt, sollte anstelle von Protocol Buffers auf JSON zurückgreifen. Das gilt insbesondere dann, wenn der serverseitige Teil der Anwendung in JavaScript geschrieben ist und wenn ein Großteil der Daten standardmäßig direkt von Browsern verarbeitet wird. Spielt hingegen die Flexibilität und Performance der Datenstruktur eine entscheidende Rolle, ist Protocol Buffers tendenziell die effizientere und bessere Lösung.

Tutorial: Praktische Einführung in Protobuf am Beispiel von Java

Protocol Buffers kann in vielen Softwareprojekten den Unterschied ausmachen, doch wie so häufig gilt es zunächst, die Besonderheiten und syntaktischen Kniffe der Serialisierungstechnologie kennen und anwenden zu lernen. Um Ihnen einen ersten Eindruck von der Syntax und dem Nachrichtenaustausch von Protobuf zu verschaffen, erklärt das folgende Tutorial die elementaren Schritte mit Protobuf – von der Definition des eigenen Formats in einer .proto-Datei bis hin zum Kompilieren der Protocol-Buffers-Strukturen. Exemplarisch wird dabei eine einfache Java-Adressbuch--Anwendung als Code-Grundlage verwendet, die Kontaktinformationen aus einer Datei lesen und in eine Datei schreiben kann. Jedem Adressbucheintrag sind dabei die Parameter „Name“, „ID“, „E-Mail-Adresse“ und „Telefonnummer“ zugeordnet.

Das eigene Datenformat in der .proto-Datei definieren

Jegliche Datenstruktur, die Sie mit Protocol Buffers realisieren möchten, beschreiben Sie zunächst in der .proto-Datei, der standardmäßigen Konfigurationsdatei des Serialisierungsformats. Für jede Struktur, die Sie in dieser Datei serialisieren – also aufeinanderfolgend abbilden – möchten, fügen Sie hierfür einfach eine Nachricht („message“) hinzu. Anschließend spezifizieren Sie Namen und Typen für jedes Feld dieser Nachricht und hängen den bzw. die gewünschten Modifikator(en) an. Ein Modifikator pro Feld ist dabei Pflicht.

Für das Java-Adressbuch sieht eine mögliche Abbildung der Datenstrukturen in der .proto-Datei folgendermaßen aus:

syntax = "proto3";
package tutorial;
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;
  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }
message PhoneNumber {
required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }
  repeated PhoneNumber phones = 4;
}
message AddressBook {
  repeated Person people = 1;
}

Die Syntax von Protocol Buffers erinnert also stark an C++ oder Java. Die Protobuf-Version wird dabei immer an erster Stelle deklariert (hier proto3), gefolgt von der Beschreibung des Softwarepakets, dessen Daten Sie strukturieren möchten. Dazu zählen ein eindeutiger Name („tutorial“) und in diesem Code-Beispiel die beiden Java-spezifischen Optionenjava_package“ (Java-Paket, in dem die generierten Klassen gespeichert werden) und „java_outer_classname“ (definiert den Klassennamen, unter dem die Klassen zusammengefasst werden).

Es folgen die Protobuf-Nachrichten, die sich aus beliebig vielen Feldern zusammensetzen können, wobei die typischen Datentypen wie „bool“, „int32“, „float“, „double“, oder „string“ verfügbar sind. Diese kommen im Beispiel auch teilweise zum Einsatz. Jedem Feld einer Message muss dabei wie bereits erwähnt mindestens ein Modifikator zugeordnet sein – also entweder …

  • required: Ein Wert für das Feld ist verpflichtend. Fehlt dieser Wert, bleibt die Message „uninitialized“, also nicht initialisiert bzw. unverschickt.
  • optional: In einem optionalen Feld kann ein Wert geliefert werden, muss aber nicht. Ist dies nicht der Fall, wird ein als Standard definierter Wert verwendet. Im voranstehenden Code ist beispielsweise der Standardwert „HOME“ (Festnetznummer Zuhause) für den Telefonnummerntyp eingetragen.
  • repeated: Felder mit dem Modifikator „repeated“ können beliebig oft wiederholt werden (einschließlich null Mal).

Eine ausführliche Anleitung zur Definition des eigenen Datenformats mit Protocol Buffers finden Sie im Google-Developer-Forum.

Das eigene Protocol-Buffers-Schema kompilieren

Sind die eigenen Datenstrukturen wie gewünscht in der .proto-Datei definiert, generieren Sie die Klassen, die zum Lesen und Schreiben der Protobuf-Nachrichten benötigt werden. Zu diesem Zweck wenden Sie den Protocol-Buffers-Compiler (protoc) auf die Konfigurationsdatei an. Sofern Sie diesen noch nicht installiert haben, laden Sie einfach die aktuelle Version im offiziellen GitHub-Repository herunter. Entpacken Sie die ZIP-Datei an gewünschter Stelle und starten den Compiler anschließend per Doppelklick (befindet sich im „bin“-Ordner).

Hinweis

Achten Sie darauf, dass Sie die passende Edition des Protobuf-Compilers herunterladen: Protoc ist wahlweise für 32- oder 64-Bit-Architekturen (Windows, Linux oder macOS) verfügbar.

Abschließend spezifizieren Sie

  • das Quell-Verzeichnis, in dem der Code Ihres Programms liegt (hier Platzhalter „SRC_DIR“),
  • das Ziel-Verzeichnis, in das der generierte Code gespeichert werden soll (hier Platzhalter „DST_DIR“)
  • und den Pfad zur .proto-Datei.

Da Sie Java-Klassen generieren möchten, nutzen Sie außerdem die Option --java_out (ähnliche Optionen gibt es auch für die anderen unterstützten Sprachen). Der vollständige Befehl für die Kompilierung lautet folgendermaßen:

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
Tipp

Ein ausführlicheres Protobuf-Java-Tutorial, in dem unter anderem auch die Nachrichtenübertragung via Protocol Buffers (Lesen/Schreiben) erklärt wird, bietet Google in der „Developers“-Sektion an, dem hauseigenen Projektbereich des Suchmaschinenriesens für Entwickler. Alternativ haben Sie dort auch Zugriff auf Anleitungen für die anderen unterstützen Sprachen wie C++, Go oder Python.