Protocol Buffers: обзор

Protocol Buffers (protobuf) — бинарный формат сериализации данных от Google, компактнее и быстрее JSON, со строгой типизацией через .proto-схему.

Зачем нужно

JSON — текстовый формат, легко читаемый человеком, но избыточный: ключи повторяются в каждом объекте, числа хранятся как строки. Protobuf кодирует данные бинарно — сообщения в 3–10 раз меньше и десериализуются значительно быстрее. Это критично для микросервисной коммуникации с высокой нагрузкой. Protobuf является транспортным форматом gRPC.

Где используется

  • gRPC — бинарный RPC-фреймворк Google (использует protobuf как единственный формат)
  • Микросервисная коммуникация с жёсткими требованиями к производительности
  • Мобильные API, где важен минимальный объём трафика
  • Kafka и другие системы обмена сообщениями (Schema Registry)

Синтаксис .proto-файла

// user.proto
syntax = "proto3";

package myapp;

// Определение сообщения
message User {
  int32 id = 1;         // номер поля (1–536870911), не менять после релиза
  string name = 2;
  string email = 3;
  UserRole role = 4;
  repeated string tags = 5;  // массив
  Address address = 6;        // вложенное сообщение
}

message Address {
  string city = 1;
  string country = 2;
}

enum UserRole {
  USER_ROLE_UNSPECIFIED = 0;  // всегда 0 как дефолт
  USER_ROLE_ADMIN = 1;
  USER_ROLE_EDITOR = 2;
}

// Сервис для gRPC
service UserService {
  rpc GetUser(GetUserRequest) returns (User);
  rpc ListUsers(ListUsersRequest) returns (stream User);
  rpc CreateUser(CreateUserRequest) returns (User);
}

message GetUserRequest { int32 id = 1; }
message ListUsersRequest { int32 page = 1; int32 limit = 2; }
message CreateUserRequest { string name = 1; string email = 2; }

Сравнение с JSON

JSON (56 байт):
{"id":42,"name":"Anton","email":"anton@example.com"}

Protobuf (34 байта — ~40% компактнее):
<binary data: field tags + varint + raw bytes>

Скорость сериализации: protobuf в ~5–6 раз быстрее JSON

Генерация кода

# Установка protoc компилятора
# https://github.com/protocolbuffers/protobuf/releases

# Генерация JavaScript/TypeScript кода
protoc --js_out=import_style=commonjs:./out user.proto

# Для TypeScript через ts-proto
protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto \
       --ts_proto_out=./out user.proto

Использование в Node.js (protobufjs)

// npm install protobufjs
const protobuf = require('protobufjs');

async function example() {
  const root = await protobuf.load('user.proto');
  const User = root.lookupType('myapp.User');

  // Кодирование
  const user = { id: 42, name: 'Антон', email: 'anton@example.com' };
  const buffer = User.encode(User.create(user)).finish;
  console.log('Размер:', buffer.byteLength, 'байт');

  // Декодирование
  const decoded = User.decode(buffer);
  console.log(decoded.name); // Антон
}

Частые ошибки

  • Изменение номера поля после релиза — ломает совместимость всех существующих сообщений
  • Пропуск USER_ROLE_UNSPECIFIED = 0 в enum — protobuf3 требует нулевое значение
  • Использование protobuf там, где достаточно JSON — избыточная сложность для простых CRUD API
  • Хранение бинарных protobuf-данных в PostgreSQL без документации схемы

Связанные темы

Ресурсы