Protocol Buffers (Protobuf) este un format binar de serializare a datelor structurate, dezvoltat de Google pentru schimbul eficient de informații între servicii. Formatul necesită definirea prealabilă a schemei în fișiere .proto, din care se generează cod pentru diferite limbaje. Conform documentației oficiale Google, Protobuf asigură dimensiuni ale mesajelor de 3-10 ori mai mici decât JSON. Protobuf este utilizat în gRPC, Google Maps și mii de servicii interne.
Principalele aspecte
Protocol Buffers este un mecanism de serializare a datelor structurate dezvoltat de Google, similar cu JSON și XML, dar cu o diferență fundamentală: datele sunt codificate în format binar. Aceasta înseamnă că mesajul Protobuf nu poate fi citit cu ochiul liber, dar ocupă mult mai puțin spațiu și este procesat mai rapid decât echivalentele text.
Protobuf a fost creat în cadrul Google pentru a rezolva problemele de performanță la schimbul de date între servicii. În 2008, tehnologia a devenit un proiect open-source cu suport pentru multiple limbaje: C++, Java, Python, Go, JavaScript, Kotlin, Swift, Dart și altele. Versiunea proto3, lansată în 2016, a simplificat sintaxa și a adăugat suport pentru mai multe limbaje.
Diferența cheie între Protobuf și JSON este necesitatea definirii schemei înainte de începerea schimbului de date. Fișierul schemei (.proto) descrie structura mesajului: câmpuri, tipuri și numere unice de câmp. Din această schemă, compilatorul protoc generează clase în limbajul țintă pentru serializare și deserializare.
Arhitectura Protobuf include trei componente cheie: limbajul de definire a schemei (.proto), compilatorul protoc și bibliotecile runtime pentru limbaje specifice. Dezvoltatorul descrie structura datelor în fișierul .proto, rulează compilarea și primește clase gata pentru lucrul cu aceste date.
Fiecare câmp dintr-un mesaj Protobuf are un număr unic (field number) — nu este un număr de ordine, ci un tag utilizat în formatul binar pentru identificarea câmpului. Numerele de la 1 la 15 se codifică cu un octet, iar de la 16 la 2047 — cu doi octeți. De aceea, câmpurile importante cu frecvență mare de utilizare ar trebui numerotate de la 1 la 15 pentru optimizarea dimensiunii.
Protobuf suportă o gamă largă de tipuri: scalare (int32, int64, float, double, bool, string, bytes), enumerări (enum), compuse (message) și speciale (oneof, map). Fiecare tip are o reprezentare binară definită, optimizată pentru scenariul de utilizare corespunzător.
| Tip .proto | Tip C++ | Java/Kotlin | Descriere |
|---|---|---|---|
| double | double | double | Număr în virgulă mobilă pe 64 de biți |
| float | float | float | Număr în virgulă mobilă pe 32 de biți |
| int32 | int32 | int | 32-bit, variable-length encoding |
| int64 | int64 | long | 64-bit, variable-length encoding |
| string | string | String | Șir UTF-8 |
| bytes | string | ByteString | Octeți arbitrari |
| bool | bool | boolean | true / false |
Protobuf oferă trei avantaje cheie față de formatele text: dimensiunea mesajului, viteza de serializare și tipizarea strictă. În sistemele cu încărcare mare și aplicațiile mobile cu trafic limitat, aceste avantaje devin critice.
Reprezentarea binară Protobuf utilizează variable-length encoding (Varint) pentru numere: numerele mici ocupă 1 octet, cele mari — până la 10 octeți. Aceasta permite codificarea eficientă a identificatorilor, flagurilor și contoarelor, care în JSON sau XML ar ocupa zeci de octeți ca șiruri text. De exemplu, numărul 150 în Protobuf ocupă 2 octeți, în JSON — 3 octeți (ca text „150"), în XML — 3 octeți + taguri.
Un avantaj suplimentar este compatibilitatea inversă. Adăugarea unui nou câmp în schemă nu strică clienții vechi: ei ignoră pur și simplu câmpurile necunoscute. Ștergerea unui câmp necesită doar rezervarea numărului său pentru a evita coliziunile în viitor.
Schema .proto descrie structura datelor într-un limbaj special. Fișierul începe cu specificarea sintaxei (proto3), a pachetului și a importurilor de dependențe. Fiecare mesaj este definit cu cuvântul cheie message, având câmpuri unde fiecare câmp are un tip, un nume și un număr unic.
Reguli pentru o schemă bună: numere de câmp de la 1 la 15 pentru câmpurile frecvent utilizate, denumiri semnificative, gruparea câmpurilor înrudite în message separate, utilizarea oneof pentru câmpurile care pot fi doar una dintre mai multe variante.
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;
}
Mesajele pot fi imbricate: câmpul profile de tip Profile va conține datele utilizatorului. Suportul pentru imbricare permite descrierea structurilor ierarhice complexe fără duplicarea definițiilor.
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;
}
Compilatorul protoc transformă fișierele .proto în cod sursă în limbajul țintă. Pentru Kotlin/Java se utilizează parametrul --java_out, pentru Swift --swift_out (prin pluginul Apple Swift Protobuf), pentru Dart --dart_out. Clasele generate conțin metode builder pentru construirea mesajelor și metode de serializare/deserializare.
Pe Android, pentru lucrul cu Protobuf se utilizează pluginul com.google.protobuf versiunea 0.9+ în Gradle. După adăugarea pluginului și specificarea fișierelor .proto, compilarea generează automat clase Kotlin gata de utilizare în aplicație.
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 {
// Serializare în format binar
val bytes = user.toByteArray()
// Deserializare din format binar
return User.parseFrom(bytes)
}
Protobuf este deosebit de eficient în arhitectura microserviciilor și aplicațiile mobile. Să considerăm un scenariu tipic: o aplicație mobilă primește o listă de produse de la server prin gRPC. Mesajul Protobuf conține informații despre produs, inclusiv identificator, denumire, preț și categorie. Formatul binar reduce dimensiunea răspunsului de 5-8 ori comparativ cu JSON.
Exemplu de serviciu gRPC cu Protobuf pe partea de server. Serviciul declară metoda RPC GetProducts, care primește o cerere cu parametrii de paginare și returnează o listă de produse. Implementarea în Kotlin utilizează clasele generate pentru lucrul cu cereri și răspunsuri.
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()
}
}
Configurarea Protobuf în proiectul Android se realizează prin pluginul Gradle com.google.protobuf. Pluginul rulează automat compilatorul protoc la compilare și generează clase Kotlin din fișierele .proto. Pentru funcționare, este necesară adăugarea pluginului în build.gradle la nivel de proiect și aplicarea acestuia în modulul aplicației.
După configurarea Gradle, fișierele .proto se plasează în directorul src/main/proto. Compilatorul protoc le procesează la fiecare compilare, generând clase Kotlin care pot fi utilizate în codul aplicației. Este important să specificați corect versiunile protobuf și protoc pentru a evita conflictele de dependențe cu alte biblioteci ale proiectului.
Pentru proiectele iOS, Protobuf se conectează prin CocoaPods sau Swift Package Manager. Pluginul Swift Protobuf generează automat structuri Swift conforme cu protocolul Codable. În proiectele Dart pentru Flutter, se utilizează pachetul protobuf, iar compilarea se realizează prin dart run protoc_plugin.
Întrebări frecvente
Protobuf — format binar cu schemă obligatorie, de 3-10 ori mai compact decât JSON. JSON — text, lizibil pentru om, nu necesită schemă. Protobuf se serializează și deserializează mai rapid, dar necesită compilarea fișierelor .proto. JSON este mai ușor de depanat și nu necesită configurare prealabilă.
Descărcați protoc din lansarea GitHub protobuf pentru platforma dvs. Pentru macOS, instalați prin brew install protobuf. Pentru Windows, descărcați arhiva zip și adăugați protoc.exe în PATH. Pentru Android/Kotlin, utilizați pluginul Gradle com.google.protobuf care rulează automat compilarea la construire.
gRPC este un framework RPC de înaltă performanță de la Google, care utilizează Protobuf ca limbaj de interfață (IDL) și format de serializare. gRPC definește servicii și metode RPC în fișiere .proto, generează cod client și server, suportă streaming și protocoale binare.
Google suportă oficial: C++, Java, Kotlin, Python, Go, Ruby, C#, PHP, JavaScript, Objective-C, Swift și Dart. Comunitatea a dezvoltat suport pentru Rust, TypeScript, Scala, Lua și alte limbaje. Pentru dezvoltarea mobilă, sunt disponibile Kotlin/Java (Android) și Swift/Objective-C (iOS).
Protobuf suportă compatibilitatea inversă prin reguli: nu modificați numerele câmpurilor, nu ștergeți câmpuri (utilizați reserved), adăugați câmpuri noi cu numere noi. Clienții vechi ignoră câmpurile necunoscute, iar clienții noi primesc valori implicite pentru câmpurile vechi lipsă.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și