Protocol Buffers (Protobuf) ist ein binäres Serialisierungsformat für strukturierte Daten, das von Google für den effizienten Informationsaustausch zwischen Diensten entwickelt wurde. Das Format erfordert eine vorherige Schemadefinition in .proto-Dateien, aus denen Code für verschiedene Sprachen generiert wird. Laut der offiziellen Google-Dokumentation bietet Protobuf Nachrichtengrößen, die 3-10 mal kleiner sind als JSON. Protobuf wird in gRPC, Google Maps und tausenden internen Diensten verwendet.
Wichtige Punkte
Protocol Buffers ist ein von Google entwickelter Mechanismus zur Serialisierung strukturierter Daten, ähnlich wie JSON und XML, aber mit einem grundlegenden Unterschied: Die Daten werden im binären Format codiert. Das bedeutet, dass eine Protobuf-Nachricht nicht mit bloßem Auge gelesen werden kann, aber deutlich weniger Speicherplatz benötigt und schneller verarbeitet wird als textbasierte Gegenstücke.
Protobuf wurde intern bei Google entwickelt, um Leistungsprobleme beim Datenaustausch zwischen Diensten zu lösen. Im Jahr 2008 wurde die Technologie zu einem Open-Source-Projekt mit Unterstützung für viele Sprachen: C++, Java, Python, Go, JavaScript, Kotlin, Swift, Dart und andere. Version proto3, die 2016 veröffentlicht wurde, vereinfachte die Syntax und fügte Unterstützung für weitere Sprachen hinzu.
Der Hauptunterschied zwischen Protobuf und JSON ist die Notwendigkeit, ein Schema zu definieren, bevor der Datenaustausch beginnt. Die Schemadatei (.proto) beschreibt die Nachrichtenstruktur: Felder, Typen und eindeutige Feldnummern. Aus diesem Schema generiert der protoc-Compiler Klassen in der Zielsprache für Serialisierung und Deserialisierung.
Die Protobuf-Architektur umfasst drei Schlüsselkomponenten: die Schemadefinitionssprache (.proto), den protoc-Compiler und Laufzeitbibliotheken für bestimmte Sprachen. Der Entwickler beschreibt die Datenstruktur in einer .proto-Datei, führt die Kompilierung durch und erhält fertige Klassen für die Arbeit mit diesen Daten.
Jedes Feld in einer Protobuf-Nachricht hat eine eindeutige Nummer (Feldnummer) — dies ist keine fortlaufende Nummer, sondern ein Tag, das im binären Format zur Identifizierung des Feldes verwendet wird. Die Nummern 1 bis 15 werden in einem Byte codiert, die Nummern 16 bis 2047 in zwei Bytes. Daher sollten wichtige Felder mit häufiger Nutzung von 1 bis 15 nummeriert werden, um die Größe zu optimieren.
Protobuf unterstützt eine breite Palette von Typen: skalare (int32, int64, float, double, bool, string, bytes), Aufzählungen (enum), zusammengesetzte (message) und spezielle Typen (oneof, map). Jeder Typ hat eine spezifische binäre Darstellung, die für den entsprechenden Anwendungsfall optimiert ist.
| .proto-Typ | C++-Typ | Java/Kotlin | Beschreibung |
|---|---|---|---|
| double | double | double | 64-Bit-Gleitkommazahl |
| float | float | float | 32-Bit-Gleitkommazahl |
| int32 | int32 | int | 32-Bit, Kodierung mit variabler Länge |
| int64 | int64 | long | 64-Bit, Kodierung mit variabler Länge |
| string | string | String | UTF-8-Zeichenkette |
| bytes | string | ByteString | Beliebige Bytes |
| bool | bool | boolean | true / false |
Protobuf bietet drei Hauptvorteile gegenüber textbasierten Formaten: Nachrichtengröße, Serialisierungsgeschwindigkeit und strenge Typisierung. In hochbelasteten Systemen und mobilen Anwendungen mit begrenztem Datenverkehr werden diese Vorteile entscheidend.
Die binäre Darstellung von Protobuf verwendet eine Kodierung mit variabler Länge (Varint) für Zahlen: kleine Zahlen belegen 1 Byte, große bis zu 10 Byte. Dies ermöglicht eine effiziente Kodierung von Identifikatoren, Flags und Zählern, die in JSON oder XML als Textstrings Dutzende von Bytes belegen würden. Beispielsweise belegt die Zahl 150 in Protobuf 2 Byte, in JSON — 3 Byte (als Text „150“), in XML — 3 Byte + Tags.
Ein zusätzlicher Vorteil ist die Rückwärtskompatibilität. Das Hinzufügen eines neuen Feldes zum Schema beschädigt keine alten Clients: Sie ignorieren einfach unbekannte Felder. Das Löschen eines Feldes erfordert lediglich das Reservieren seiner Nummer, um zukünftige Kollisionen zu vermeiden.
Das .proto-Schema beschreibt die Datenstruktur in einer speziellen Sprache. Die Datei beginnt mit der Angabe der Syntax (proto3), des Pakets und der Import-Abhängigkeiten. Jede Nachricht wird mit dem Schlüsselwort message mit Feldern definiert, wobei jedes Feld einen Typ, Namen und eine eindeutige Nummer hat.
Gute Schema-Praktiken: Feldnummern 1 bis 15 für häufig verwendete Felder, aussagekräftige Namen, Gruppierung verwandter Felder in einer separaten message, Verwendung von oneof für Felder, die nur eine von mehreren Optionen sein können.
syntax = "proto3";
package mobileapp;
message User {
string user_id = 1;
string name = 2;
string email = 3;
int32 age = 4;
UserRole role = 5;
repeated string tags = 6;
map<string, string> metadata = 7;
}
enum UserRole {
USER_ROLE_UNSPECIFIED = 0;
USER_ROLE_USER = 1;
USER_ROLE_ADMIN = 2;
USER_ROLE_MODERATOR = 3;
}
Nachrichten können verschachtelt sein: Ein profile-Feld vom Typ Profile enthält intern Benutzerdaten. Die Unterstützung von Verschachtelungen ermöglicht die Beschreibung komplexer hierarchischer Strukturen ohne Duplizierung von Definitionen.
syntax = "proto3";
package mobileapp;
message Order {
string order_id = 1;
repeated OrderItem items = 2;
double total_price = 3;
PaymentInfo payment = 4;
}
message OrderItem {
string product_id = 1;
string title = 2;
int32 quantity = 3;
double price = 4;
}
message PaymentInfo {
string method = 1;
string transaction_id = 2;
double amount = 3;
}
Der protoc-Compiler konvertiert .proto-Dateien in Quellcode in der Zielsprache. Für Kotlin/Java wird der Parameter --java_out verwendet, für Swift --swift_out (über das Apple Swift Protobuf Plugin), für Dart --dart_out. Die generierten Klassen enthalten Builder-Methoden zum Konstruieren von Nachrichten sowie Methoden zur Serialisierung/Deserialisierung.
Auf Android wird für die Arbeit mit Protobuf das Plugin com.google.protobuf Version 0.9+ in Gradle verwendet. Nach dem Hinzufügen des Plugins und der Angabe der .proto-Dateien generiert der Build automatisch Kotlin-Klassen, die in der Anwendung verwendet werden können.
import com.google.protobuf.kotlin.toByteString
import com.example.mobileapp.UserOuterClass.User
fun createUser(): User {
return User.newBuilder()
.setUserId("usr_001")
.setName("IT Sectr")
.setEmail("team@itsectr.com")
.setAge(5)
.setRole(UserOuterClass.UserRole.USER_ROLE_ADMIN)
.addTags("mobile")
.addTags("backend")
.build()
}
fun serializeAndDeserialize(user: User): User {
// Serialisierung in das binäre Format
val bytes = user.toByteArray()
// Deserialisierung aus dem binären Format
return User.parseFrom(bytes)
}
Protobuf ist besonders effektiv in der Mikrodienste-Architektur und in mobilen Anwendungen. Betrachten wir ein typisches Szenario: Eine mobile Anwendung erhält eine Produktliste vom Server über gRPC. Die Protobuf-Nachricht enthält Produktinformationen einschließlich ID, Name, Preis und Kategorie. Das binäre Format reduziert die Antwortgröße um das 5- bis 8-fache im Vergleich zu JSON.
Beispiel eines gRPC-Dienstes mit Protobuf auf der Serverseite. Der Dienst deklariert die RPC-Methode GetProducts, die eine Anfrage mit Paginierungsparametern akzeptiert und eine Produktliste zurückgibt. Die Kotlin-Implementierung verwendet generierte Klassen für die Arbeit mit Anfragen und Antworten.
import com.example.mobileapp.ProductServiceGrpcKt
import com.example.mobileapp.ProductOuterClass.Product
import com.example.mobileapp.ProductOuterClass.GetProductsRequest
import com.example.mobileapp.ProductOuterClass.GetProductsResponse
class ProductService : ProductServiceGrpcKt.ProductServiceCoroutineImplBase() {
override suspend fun getProducts(
request: GetProductsRequest
): GetProductsResponse {
val products = fetchProductsFromDb(
page = request.page,
limit = request.limit
)
return GetProductsResponse.newBuilder()
.addAllProducts(products)
.setTotalCount(products.size)
.build()
}
}
Das Einrichten von Protobuf in einem Android-Projekt erfolgt über das Gradle-Plugin com.google.protobuf. Das Plugin führt während des Builds automatisch den protoc-Compiler aus und generiert Kotlin-Klassen aus .proto-Dateien. Für die Arbeit müssen Sie das Plugin zur build.gradle auf Projektebene hinzufügen und im Anwendungsmodul anwenden.
Nach der Konfiguration von Gradle werden die .proto-Dateien im Verzeichnis src/main/proto abgelegt. Der protoc-Compiler verarbeitet sie bei jedem Build und generiert Kotlin-Klassen, die im Anwendungscode verwendet werden können. Es ist wichtig, die Versionen von protobuf und protoc korrekt anzugeben, um Abhängigkeitskonflikte mit anderen Projektbibliotheken zu vermeiden.
Für iOS-Projekte wird Protobuf über CocoaPods oder Swift Package Manager eingebunden. Das Swift Protobuf Plugin generiert automatisch Swift-Strukturen, die dem Codable-Protokoll entsprechen. In Dart-Projekten für Flutter wird das Paket protobuf verwendet, und die Kompilierung erfolgt über dart run protoc_plugin.
Häufig gestellte Fragen
Protobuf ist ein binäres Format mit obligatorischem Schema, 3-10 mal kompakter als JSON. JSON ist textbasiert, menschenlesbar und benötigt kein Schema. Protobuf serialisiert und deserialisiert schneller, erfordert jedoch die Kompilierung von .proto-Dateien. JSON ist einfacher zu debuggen und erfordert keine vorherige Einrichtung.
Laden Sie protoc vom GitHub-Release von protobuf für Ihre Plattform herunter. Für macOS installieren Sie es über brew install protobuf. Für Windows laden Sie das ZIP-Archiv herunter und fügen protoc.exe zum PATH hinzu. Für Android/Kotlin verwenden Sie das Gradle-Plugin com.google.protobuf, das die Kompilierung während des Builds automatisch ausführt.
gRPC ist ein Hochleistungs-RPC-Framework von Google, das Protobuf als Schnittstellendefinitionssprache (IDL) und Serialisierungsformat verwendet. gRPC definiert Dienste und RPC-Methoden in .proto-Dateien, generiert Client- und Servercode und unterstützt Streaming und binäre Protokolle.
Offiziell von Google unterstützt: C++, Java, Kotlin, Python, Go, Ruby, C#, PHP, JavaScript, Objective-C, Swift und Dart. Die Community hat Unterstützung für Rust, TypeScript, Scala, Lua und andere Sprachen entwickelt. Für die mobile Entwicklung sind Kotlin/Java (Android) und Swift/Objective-C (iOS) verfügbar.
Protobuf unterstützt die Rückwärtskompatibilität durch folgende Regeln: Ändern Sie keine Feldnummern, löschen Sie keine Felder (verwenden Sie reserved), fügen Sie neue Felder mit neuen Nummern hinzu. Alte Clients ignorieren unbekannte Felder, neue Clients erhalten Standardwerte für fehlende alte Felder.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch