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) — это не порядковый номер, а тег, используемый в бинарном формате для идентификации поля. Номера от 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 — бинарный формат с обязательной схемой, компактнее JSON в 3-10 раз. 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), добавляйте новые поля с новыми номерами. Старые клиенты игнорируют неизвестные поля, новые клиенты получают default-значения для отсутствующих старых полей.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также