← Назад к вопросам

Для чего используешь Protocol Buffer

2.0 Middle🔥 91 комментариев
#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Protocol Buffers: Назначение и использование

Protocol Buffers (protobuf) — это метод сериализации структурированных данных, разработанный Google. Это универсальный язык для описания структур данных с автоматической генерацией кода для различных языков программирования.

Основное назначение Protocol Buffers

1. Сериализация и десериализация

Проблема без protobuf:

// JSON (человеческий, но тяжёлый)
{
  "id": 123,
  "name": "John",
  "email": "john@example.com",
  "age": 30
}
// Размер: 76 байт

// XML (ещё тяжелее)
<?xml version="1.0"?>
<user>
  <id>123</id>
  <name>John</name>
  <email>john@example.com</email>
  <age>30</age>
</user>
// Размер: 132 байта

Решение с protobuf:

// user.proto
syntax = "proto3";

message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
  int32 age = 4;
}

// Бинарная сериализация: ~20 байт
// Очень компактно!

2. Язык описания (IDL)

Protobuf использует свой язык для описания структур данных:

// address.proto
syntax = "proto3";

package com.example.model;

message Address {
  string street = 1;
  string city = 2;
  string country = 3;
  string postal_code = 4;
}

message Person {
  int32 id = 1;
  string name = 2;
  repeated string phone_numbers = 3;
  Address home_address = 4;
  map<string, string> attributes = 5;
}

Компилируя это с protoc, получаешь Java классы:

// Автогенерированный код
public class Person {
    private int id;
    private String name;
    private List<String> phoneNumbers;
    private Address homeAddress;
    private Map<String, String> attributes;
    
    // getters, setters, equals, hashCode, toString
    // методы сериализации/десериализации
}

Практические примеры использования

Пример 1: gRPC (Google's RPC framework)

Protobuf идеально работает с gRPC для микросервисов:

// user_service.proto
syntax = "proto3";

package com.example.service;

message GetUserRequest {
  int32 user_id = 1;
}

message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
}

message CreateUserRequest {
  string name = 1;
  string email = 2;
}

message CreateUserResponse {
  bool success = 1;
  User user = 2;
}

service UserService {
  rpc GetUser(GetUserRequest) returns (User);
  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
  rpc ListUsers(Empty) returns (stream User);
}

В Java с помощью gRPC:

// Сервер
public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {
    @Override
    public void getUser(GetUserRequest request, StreamObserver<User> responseObserver) {
        User user = userRepository.findById(request.getUserId());
        responseObserver.onNext(user);
        responseObserver.onCompleted();
    }
    
    @Override
    public void createUser(CreateUserRequest request, 
                          StreamObserver<CreateUserResponse> responseObserver) {
        User user = new User.Builder()
            .setId(123)
            .setName(request.getName())
            .setEmail(request.getEmail())
            .build();
        
        CreateUserResponse response = CreateUserResponse.newBuilder()
            .setSuccess(true)
            .setUser(user)
            .build();
        
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

// Клиент
public class UserServiceClient {
    private UserServiceGrpc.UserServiceBlockingStub stub;
    
    public void getUser(int userId) {
        GetUserRequest request = GetUserRequest.newBuilder()
            .setUserId(userId)
            .build();
        
        User user = stub.getUser(request);
        System.out.println("User: " + user.getName());
    }
}

Пример 2: Микросервисная архитектура

// order.proto
syntax = "proto3";

package com.example.orders;

message OrderItem {
  int32 product_id = 1;
  int32 quantity = 2;
  double price = 3;
}

message Order {
  int32 id = 1;
  int32 customer_id = 2;
  repeated OrderItem items = 3;
  double total_price = 4;
  OrderStatus status = 5;
  int64 created_at = 6;  // Unix timestamp
}

enum OrderStatus {
  UNKNOWN = 0;
  PENDING = 1;
  PROCESSING = 2;
  SHIPPED = 3;
  DELIVERED = 4;
}

message OrderEvent {
  int32 order_id = 1;
  OrderStatus new_status = 2;
  string message = 3;
}

Использование в Kafka:

// Продюсер
KafkaProducer<String, OrderEvent> producer = new KafkaProducer<>();

Order order = Order.newBuilder()
    .setId(1)
    .setCustomerId(100)
    .addItems(OrderItem.newBuilder()
        .setProductId(5)
        .setQuantity(2)
        .setPrice(29.99)
        .build())
    .setTotalPrice(59.98)
    .setStatus(OrderStatus.PENDING)
    .setCreatedAt(System.currentTimeMillis())
    .build();

OrderEvent event = OrderEvent.newBuilder()
    .setOrderId(order.getId())
    .setNewStatus(OrderStatus.PROCESSING)
    .setMessage("Order is being processed")
    .build();

ProducerRecord<String, OrderEvent> record = new ProducerRecord<>(
    "order-events",
    String.valueOf(order.getId()),
    event
);

producer.send(record);

// Консюмер
KafkaConsumer<String, OrderEvent> consumer = new KafkaConsumer<>();
consumer.subscribe(List.of("order-events"));

ConsumerRecords<String, OrderEvent> records = consumer.poll(Duration.ofSeconds(1));

for (ConsumerRecord<String, OrderEvent> record : records) {
    OrderEvent event = record.value();
    System.out.println("Order " + event.getOrderId() + 
                       " status: " + event.getNewStatus());
}

Пример 3: REST API с protobuf

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    
    // Spring может работать с protobuf
    @PostMapping(consumes = "application/x-protobuf",
                 produces = "application/x-protobuf")
    public UserProto.User createUser(@RequestBody UserProto.CreateUserRequest request) {
        return userService.createUser(request);
    }
    
    @GetMapping(value = "/{id}", produces = "application/x-protobuf")
    public UserProto.User getUser(@PathVariable int id) {
        return userService.getUserById(id);
    }
    
    @GetMapping(produces = "application/x-protobuf")
    public UserProto.UserList listUsers() {
        return userService.listAllUsers();
    }
}

Когда использовать Protocol Buffers

СценарийПодходит?Причина
gRPC микросервисыДаИдеальная пара
Асинхронная очередь (Kafka/RabbitMQ)ДаКомпактность + производительность
REST API с JSONНетJSON проще для веба
Real-time сокеты (WebSocket)Может бытьНужна компрессия с JSON
Мобильные приложенияДаЭкономия трафика
IoT приложенияДаУзкополосное соединение
BigData (Hadoop, Spark)ДаКомпактная сериализация
Хранилище в БДМожет бытьЕсли нужна компактность

Преимущества Protocol Buffers

1. Компактность

// JSON: 150 байт
{"id": 1, "name": "John", "email": "john@example.com", "age": 30}

// Protobuf: ~20 байт (бинарно)
// Экономия: 87% трафика!

2. Скорость сериализации

// Benchmark
String json = "{\"id\": 1, \"name\": \"John\"}";
long start = System.nanoTime();

for (int i = 0; i < 1_000_000; i++) {
    // JSON парсинг: 500мс
    User user = jsonMapper.readValue(json, User.class);
    
    // Protobuf парсинг: 50мс (10x быстрее!)
    User user2 = User.parseFrom(protobufBytes);
}

3. Backward & Forward Compatibility

// v1
message User {
  int32 id = 1;
  string name = 2;
}

// v2 (расширили)
message User {
  int32 id = 1;
  string name = 2;
  string email = 3;  // Новое поле
}

// Старый код может читать новый protobuf (проигнорирует email)
// Новый код может читать старый protobuf (email будет пусто)

4. Code Generation

# Одна команда генерирует Java/Go/Python/C++/etc
protoc --java_out=. user.proto

# Получаешь готовые классы с getters, setters, equals, hashCode, toString

Недостатки Protocol Buffers

// Минусы:
// 1. Не человекочитаемый формат
byte[] data = user.toByteArray();
System.out.println(Arrays.toString(data));  // [10, 5, 74, 111, 104, 110, ...]

// 2. Нужен protoc компилятор
// 3. Кривая обучения выше, чем JSON
// 4. Web браузеры не поддерживают natively (как JSON)
// 5. Debug сложнее

Практический workflow

1. Создай .proto файл

// src/main/proto/user.proto
syntax = "proto3";

package com.example.user;

message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
  repeated string tags = 4;
}

2. Добавь в Maven

<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.21.0</version>
</dependency>

<plugin>
    <groupId>org.xolstice.maven.plugins</groupId>
    <artifactId>protobuf-maven-plugin</artifactId>
    <version>0.6.1</version>
    <configuration>
        <protocArtifact>com.google.protobuf:protoc:3.21.0:exe:${os.detected.classifier}</protocArtifact>
    </configuration>
</plugin>

3. Используй автогенерированные классы

User user = User.newBuilder()
    .setId(1)
    .setName("John")
    .setEmail("john@example.com")
    .addTags("java")
    .addTags("developer")
    .build();

// Сериализация
byte[] serialized = user.toByteArray();

// Десериализация
User deserialized = User.parseFrom(serialized);

Выводы

Protocol Buffers используются для:

  1. gRPC — идеальная пара для микросервисов
  2. Асинхронная коммуникация — Kafka, RabbitMQ
  3. Мобильные приложения — экономия трафика
  4. IoT — узкополосные каналы
  5. BigData — компактная сериализация

Выбор:

  • JSON — для REST API, веб
  • Protobuf — для RPC, микросервисов, мобильных
  • Avro — для Hadoop/Spark pipelines

Protobuf — это инвестиция в производительность и совместимость версий, но только если это реально нужно.

Для чего используешь Protocol Buffer | PrepBro