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 та бібліотеки часу виконання для конкретних мов. Розробник описує структуру даних в .proto файлі, запускає компіляцію та отримує готові класи для роботи з цими даними.
Кожне поле в повідомленні Protobuf має унікальний номер (номер поля) — це не порядковий номер, а тег, що використовується в бінарному форматі для ідентифікації поля. Номери від 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-бітне число з плаваючою крапкою |
| float | float | float | 32-бітне число з плаваючою крапкою |
| int32 | int32 | int | 32-біт, кодування змінної довжини |
| int64 | int64 | long | 64-біт, кодування змінної довжини |
| string | string | String | Рядок UTF-8 |
| bytes | string | ByteString | Довільні байти |
| bool | bool | boolean | true / false |
Protobuf дає три ключові переваги порівняно з текстовими форматами: розмір повідомлення, швидкість серіалізації та сувора типізація. У високонавантажених системах та мобільних додатках з обмеженим трафіком ці переваги стають критичними.
Бінарне представлення Protobuf використовує кодування змінної довжини (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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також