Protocol Buffers (Protobuf) — је бинарни формат серијализације структурираних података, који је развио Google за ефикасну размену информација између сервиса. Формат захтева претходно дефинисање шеме у .proto датотекама, из којих се генерише код за различите језике. Према службеној документацији Google-а, Protobuf обезбеђује величину поруке 3-10 пута мању од JSON. Protobuf се користи у gRPC, Google Maps и хиљадама унутрашњих сервиса.
Главно
Protocol Buffers — је механизам серијализације структурираних података, који је развио Google, сличан JSON-у и XML-у, али са суштинском разликом: подаци су кодирани у бинарни формат. То значи да се порука Protobuf-а не може прочитати голим оком, али заузима знатно мање места и обрађује се брже од текстуалних аналога.
Protobuf је настао унутар Google-а за решавање проблема перформанси при размени података између сервиса. 2008. године технологија је постала отворени пројекат са подршком за многе језике: C++, Java, Python, Go, JavaScript, Kotlin, Swift, Dart и друге. Верзија proto3, објављена 2016. године, поједноставила је синтаксу и додала подршку за већи број језика.
Кључна разлика између Protobuf-а и JSON-а — потреба за дефинисањем шеме пре почетка размене података. Датотека шеме (.proto) описује структуру поруке: поља, типове и јединствене бројеве поља. Из ове шеме, компајлер protoc генерише класе у циљном језику за серијализацију и десеријализацију.
Архитектура Protobuf-а обухвата три кључне компоненте: језик дефиниције шеме (.proto), компајлер protoc и runtime библиотеке за конкретне језике. Програмер описује структуру података у .proto датотеци, покреће компилацију и добија готове класе за рад са овим подацима.
Свако поље у поруци Protobuf-а има јединствени број (field number) — то није редни број, већ tag који се у бинарном формату користи за идентификацију поља. Бројеви од 1 до 15 кодирају се једним бајтом, од 16 до 2047 — са два бајта. Због тога важна поља са високом фреквенцијом коришћења треба нумерисати од 1 до 15 ради оптимизације величине.
Protobuf подржава широк спектар типова: скаларне (int32, int64, float, double, bool, string, bytes), набрајања (enum), сложене (message) и специјалне (oneof, map). Сваки тип има одређену бинарну репрезентацију, оптимизовану за одговарајући сценарио коришћења.
| Тип .proto | C++ тип | Java/Kotlin | Опис |
|---|---|---|---|
| double | double | double | 64-bit број с покретним зарезом |
| float | float | float | 32-bit број с покретним зарезом |
| int32 | int32 | int | 32-bit, variable-length encoding |
| int64 | int64 | long | 64-bit, variable-length encoding |
| string | string | String | UTF-8 стринг |
| bytes | string | ByteString | Произвољни бајтови |
| bool | bool | boolean | true / false |
Protobuf даје три кључне предности у поређењу са текстуалним форматима: величину поруке, брзину серијализације и строго типовање. У високо оптерећеним системима и мобилним апликацијама са ограниченим саобраћајем, ове предности постају критичне.
Бинарна репрезентација Protobuf-а користи variable-length encoding (Varint) за бројеве: мали бројеви заузимају 1 бајт, велики — до 10 бајтова. Ово омогућава ефикасно кодирање идентификатора, заставица и бројача, који би у JSON или XML заузели десетине бајтова као текстуални стрингови. На пример, број 150 у Protobuf-у заузима 2 бајта, у JSON-у 3 бајта (као текст „150"), у XML-у 3 бајта + ознаке.
Додатна предност је уназад компатибилност. Додавање новог поља у шему не квари старе клијенте: они једноставно игноришу непозната поља. Брисање поља захтева само резервисање његовог броја да би се избегле колизије у будућности.
.proto шема описује структуру података на специјалном језику. Датотека почиње навођењем синтаксе (proto3), пакета и импорта зависности. Свака порука се дефинише кључном речи message са пољима, где свако поље има тип, име и јединствени број.
Правила добре шеме: бројеви поља од 1 до 15 за често коришћена поља, смислена имена, груписање повезаних поља у посебне message, коришћење oneof за поља која могу бити само једна од неколико варијанти.
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;
}
Поруке могу бити угнежђене: поље profile типа Profile ће садржати податке корисника. Подршка за угнежђивање омогућава описивање сложених хијерархијских структура без понављања дефиниција.
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;
}
Компајлер protoc претвара .proto датотеке у изворни код на циљном језику. За Kotlin/Java користи се параметар --java_out, за Swift --swift_out (преко Apple Swift Protobuf додатка), за Dart --dart_out. Генерисане класе садрже builder методе за конструисање порука и методе серијализације/десеријализације.
На Android-у се за рад са Protobuf-ом користи додатак com.google.protobuf верзије 0.9+ у Gradle-у. Након додавања додатка и навођења .proto датотека, компилација аутоматски генерише Kotlin класе спремне за коришћење у апликацији.
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 {
// Серијализација у бинарни формат
val bytes = user.toByteArray()
// Десеријализација из бинарног формата
return User.parseFrom(bytes)
}
Protobuf је посебно ефикасан у микросервисној архитектури и мобилним апликацијама. Размотримо типичан сценарио: мобилна апликација добија листу производа од сервера преко gRPC-а. Порука Protobuf-а садржи информације о производу, укључујући идентификатор, назив, цену и категорију. Бинарни формат смањује величину одговора 5-8 пута у поређењу са JSON-ом.
Пример gRPC сервиса са Protobuf-ом на серверској страни. Сервис декларише RPC метод GetProducts, који прима захтев са параметрима пагинације и враћа листу производа. Имплементација на Kotlin-у користи генерисане класе за рад са захтевима и одговорима.
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()
}
}
Подешавање Protobuf-а у Android пројекту се врши преко Gradle додатка com.google.protobuf. Додатак аутоматски покреће компајлер protoc при компилацији и генерише Kotlin класе из .proto датотека. За рад је потребно додати додатак у build.gradle нивоа пројекта и применити га у модулу апликације.
Након подешавања Gradle-а, .proto датотеке се смештају у директоријум src/main/proto. Компајлер protoc их обрађује при свакој компилацији, генеришући Kotlin класе које се могу користити у коду апликације. Важно је правилно навести верзију protobuf и protoc да би се избегли конфликти зависности са другим библиотекама пројекта.
За iOS пројекте, Protobuf се повезује преко CocoaPods или Swift Package Manager-а. Додатак Swift Protobuf аутоматски генерише Swift структуре у складу са протоколом Codable. У Dart пројектима за Flutter користи се пакет protobuf, а компилација се врши преко dart run protoc_plugin.
Често постављана питања
Protobuf — бинарни формат са обавезном шемом, 3-10 пута компактнији од JSON-а. JSON — текстуални, читљив за човека, не захтева шему. Protobuf се брже серијализује и десеријализује, али захтева компилацију .proto датотека. JSON је лакши за отклањање грешака и не захтева претходно подешавање.
Преузмите protoc са GitHub издања protobuf-а за вашу платформу. За macOS инсталирајте преко brew install protobuf. За Windows преузмите zip архиву и додајте protoc.exe у PATH. За Android/Kotlin користите Gradle додатак com.google.protobuf који аутоматски покреће компилацију при изградњи.
gRPC — је високоперформансни RPC оквир од Google-а, који користи Protobuf као интерфејсни језик (IDL) и формат серијализације. gRPC дефинише сервисе и RPC методе у .proto датотекама, генерише клијентски и серверски код, подржава стримовање и бинарне протоколе.
Google званично подржава: C++, Java, Kotlin, Python, Go, Ruby, C#, PHP, JavaScript, Objective-C, Swift и Dart. Заједница је развила подршку за Rust, TypeScript, Scala, Lua и друге језике. За мобилни развој доступни су Kotlin/Java (Android) и Swift/Objective-C (iOS).
Protobuf подржава уназад компатибилност кроз правила: не мењајте бројеве поља, не бришите поља (користите reserved), додајте нова поља са новим бројевима. Стари клијенти игноришу непозната поља, нови клијенти добијају подразумеване вредности за недостајућа стара поља.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође