Справочник по gRPC
Назначение
Методы, коды ответов, заголовки и форматы gRPC в табличном виде. Учебный курс: REST, GraphQL и gRPC — стили API, раздел 8.05.
Краткое пояснение
Прототип .proto-файла в proto3 (текущая рекомендованная версия):
Быстрый старт
protoc \
--proto_path=src/proto \
--csharp_out=gen/csharp \
--csharp_opt=base_namespace=Example.V1 \
--grpc_out=gen/csharp \
--plugin=protoc-gen-grpc=/usr/local/bin/grpc_csharp_plugin \
src/proto/user_service.proto
Разбор:
protocгенерирует C#/Go/Java-код из.proto; флаги--csharp_outи--grpc_outдобавляют stubs для gRPC.
Справочные таблицы
Содержание справочника
- 1. Общая структура
.proto-файла - 2. Директивы верхнего уровня
- Основные file-level options
- 3. Сообщения (
message) - Базовая структура
- 4. Составные типы полей
- 4.1.
oneof - 4.2.
map<K, V> - 4.3.
repeated - 5.
enum - Базовый синтаксис
- Правила и ограничения
- Опции enum и enum-value
- Совместимость
- JSON mapping
- 6.
reserved - Применение
- Где можно использовать
- Важно
- 7. Extensions (только proto2, deprecated)
- Синтаксис (proto2)
- Использование в коде (Java, proto2)
- Почему deprecated
- 8. Well-Known Types (google.protobuf.*)
- Пример:
Any - 9. Сервисы и методы
- 9.1. Объявление сервиса
- 9.2. Типы методов
- 10. Опции методов и сервисов
- 10.1. Стандартные и широко используемые опции
- 11. Соглашения по именованию и структуре (AIP, Google API Style Guide)
- 11.1. Имена пакетов и файлов
- 11.2. Имена сообщений и полей
- 11.3. Стандартные сообщения (AIP-131/132/133/134/135)
- 12. Кастомные опции (custom options)
- 12.1. Объявление кастомной опции
- 12.2. Использование
- 12.3. Практическое применение
- 13. Эволюция схемы: правила обратной и прямой совместимости
- 13.1. Безопасные изменения (forward & backward compatible)
- 13.2. Небезопасные изменения (ломают совместимость)
- 13.3. Стратегии миграции
- 14. Обработка ошибок
- 14.1. Стандартный статус gRPC (
grpc.Status) - 14.2. Детализация ошибки:
google.rpc.Status - 14.3. Передача ошибок в клиентах
- 15. Кодогенерация: инструменты, плагины и стратегии
- 15.1. Базовый инструментарий
- 15.2. Популярные генераторы (альтернативы официальным)
- 15.3. Контроль версий схем:
buf.lockиdeps - 16. Транспорт и wire format
- 16.1. Форматы сериализации
- 16.2. Сжатие
- 16.3. HTTP/2 и multiplexing
- 17. Безопасность
- 17.1. Транспортный уровень
- 17.2. Аутентификация и авторизация
- 17.3. Шифрование данных (application-level)
- 18. Тестирование и отладка
- 18.1. Инструменты командной строки
- 18.2. Reflection и introspection
- 18.3. Unit/integration тестирование
- 19. Производительность и тюнинг
- 19.1. Настройки вызова (CallOptions / CallCredentials)
- 19.2. Streaming: управление потоком и batching
- 19.3. Пулы и ресурсы
- 20. Наблюдаемость (Observability)
- 20.1. Tracing (OpenTelemetry)
- 20.2. Metrics (Prometheus)
- 20.3. Логгирование
- 21. Развёртывание и инфраструктура
- 21.1. Прокси и шлюзы
- 21.2. Health checks
- 21.3. CI/CD и проверка совместимости
- 22. Миграция с REST/SOAP и гибридные API
- 22.1. Стратегии миграции
- 22.2. Преимущества гибридного подхода
- 22.3. Проблемы
- 23. Анти-паттерны и частые ошибки
1. Общая структура .proto-файла
Прототип .proto-файла в proto3 (текущая рекомендованная версия):
Код ITЗагрузка примера кода…
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
2. Директивы верхнего уровня
| Директива | Описание | Поддержка |
|---|---|---|
syntax = "proto2" / "proto3" | Указывает версию протобафа. Обязательна. | proto2/proto3 |
package <name> | Логическое пространство имён. Влияет на имена классов в кодогенерации. | Обе |
import "<path>" | Импорт других .proto. Импортируемые файлы должны быть доступны в --proto_path. | Обе |
option <name> = <value>; | Глобальные (файловые) опции. | Обе |
extend ... | Расширения сообщений (только proto2, deprecated в proto3). | Только proto2 |
Основные file-level options
| Опция | Тип | Значение по умолчанию / примечание | Где влияет |
|---|---|---|---|
java_package | string | Не задан → берётся package. Рекомендуется явно указывать. | Java-генерация |
java_outer_classname | string | Базовое имя файла + OuterClass. | Java — имя внешнего класса-контейнера |
java_multiple_files | bool | false | true → каждый message/service как отдельный класс |
java_string_check_utf8 | bool | false | true → проверка корректности UTF-8 при парсинге строк |
optimize_for | enum (SPEED, CODE_SIZE, LITE_RUNTIME) | SPEED | Влияет на объём и скорость сгенерированного кода (proto2/proto3) |
go_package | string | Не задан → package. Формат: "path;alias" | Go — путь импорта и псевдоним пакета |
csharp_namespace | string | Не задан → package. | C# — namespace |
objc_class_prefix | string | Обязателен в proto3 для Objective-C. Должен быть 3+ буквы, уникальный. | Swift/ObjC |
php_namespace | string | Не задан → package. | PHP |
php_metadata_namespace | string | — | PHP: где хранить метаданные |
php_class_prefix | string | — | PHP: префикс имён классов |
ruby_package | string | — | Ruby |
swift_prefix | string | — | Swift |
deprecated | bool | false | Помечает файл как устаревший (влияет на генерируемый код, если поддерживается языком) |
⚠️ Некоторые опции (например,
cc_api_version,cc_generic_services) устарели или не поддерживаются в proto3 (в частности, generic services отключены по умолчанию и deprecated).
3. Сообщения (message)
Базовая структура
message User {
option (my_file_options).id = 123;
option deprecated = true;
string email = 1 [(validate.rules).string.email = true];
int32 age = 2 [jstype = JS_STRING];
oneof contact_info {
string phone = 3;
string telegram = 4;
}
repeated string tags = 5 [packed = true];
map<string, string> metadata = 6;
}
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
3.1. Поля сообщения
Формат поля:
[field_behavior] [field_type] field_name = field_number [field_options];
Разбор:
- Фрагмент иллюстрирует контракт, конфигурацию или протокол обмена — сверяйте каждую строку с текстом раздела выше.
| Компонент | Обязательный? | Описание |
|---|---|---|
field_behavior | Нет (только proto3) | optional (proto3), required (proto2 only), repeated |
field_type | Да | scalar, message, enum, map<...>, oneof-члены |
field_name | Да | snake_case по convention (но не enforced) |
field_number | Да | int ≥ 1. 1–15 — 1 байт в wire format, 16–2047 — 2 байта. Зарезервированы 19000–19999 (wire type tags). |
field_options | Нет | [key = value, ...] |
3.1.1. field_behavior
| Значение | proto2 | proto3 | Примечание |
|---|---|---|---|
required | ✅ | ❌ | Deprecated даже в proto2 (начиная с Protobuf 3.3.0; генерирует warnings). Избегать. |
optional | ✅ (по умолчанию) | ✅ (явно с proto 3.12+, включено по умолчанию с 3.15) | В proto3: включает presence tracking (has_field). |
repeated | ✅ | ✅ | Для скаляров в proto3: packed=true по умолчанию. |
🔹 В proto3 до 3.12:
optionalнельзя было писать (ноoptional-поля существовали в runtime черезHasField). С 3.15 —optionalвключён по умолчанию (т.е.optional int32 x = 1;допустим).🔹
requiredсчитается антипаттерном: нарушает forward/backward совместимость (удаление → поломка парсинга).
3.1.2. Scalar types
| .proto type | C++ | Java | Python | Go | C# | Dart | wire type | JSON encode |
|---|---|---|---|---|---|---|---|---|
double | double | double | float | float64 | double | double | 1 | number |
float | float | float | float | float32 | float | double | 5 | number |
int32 | int32 | int | int | int32 | int | int | 0 | number |
int64 | int64 | long | int | int64 | long | int | 0 | string¹ |
uint32 | uint32 | int | int | uint32 | uint | int | 0 | number |
uint64 | uint64 | long | int | uint64 | ulong | int | 0 | string¹ |
sint32 | int32 | int | int | int32 | int | int | 0 | number |
sint64 | int64 | long | int | int64 | long | int | 0 | string¹ |
fixed32 | uint32 | int | int | uint32 | uint | int | 5 | number |
fixed64 | uint64 | long | int | uint64 | ulong | int | 1 | string¹ |
sfixed32 | int32 | int | int | int32 | int | int | 5 | number |
sfixed64 | int64 | long | int | int64 | long | int | 1 | string¹ |
bool | bool | boolean | bool | bool | bool | bool | 0 | true/false |
string | string | String | str/unicode | string | string | String | 2 | string |
bytes | string | ByteString | bytes | []byte | ByteString | Uint8List | 2 | base64 string |
¹ — Для int64, uint64, fixed64, sfixed64 в JSON: строка, чтобы избежать потери точности в JS (Number.MAX_SAFE_INTEGER = 2⁵³−1). Это регулируется опцией jstype (см. ниже).
3.1.3. jstype (field option, proto3 only)
Управляет JSON-представлением 64-битных типов:
int64 id = 1 [jstype = JS_STRING]; // → "1234567890123456789"
int64 id = 1 [jstype = JS_NUMBER]; // → 1234567890123456789 (потенциально unsafe!)
int64 id = 1 [jstype = JS_NORMAL]; // → как по умолчанию (строка)
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
| Значение | JSON | Риски |
|---|---|---|
JS_NORMAL (default) | "123" | Безопасно |
JS_STRING | "123" | Безопасно, явно |
JS_NUMBER | 123 | Потеря точности в JS при значении > 2⁵³−1 |
4. Составные типы полей
4.1. oneof
Группа полей, из которых только одно может быть установлено в один момент времени. Используется для union-подобной семантики.
Синтаксис
message LoginRequest {
oneof method {
string username = 1;
string email = 2;
int64 phone = 3;
}
string password = 4;
}
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
Особенности
| Свойство | Описание |
|---|---|
Имя oneof | Опционально. Без имени — анонимный oneof (не рекомендуется). Имя используется в сгенерированном коде (WhichOneof("method")). |
| Поля внутри | Любые типы, включая message, enum, scalar. Но не repeated, map, group (deprecated). |
| Field numbers | Общее пространство с другими полями сообщения — не должны пересекаться. |
| Наследование | oneof не может быть унаследован (в protobuf нет наследования сообщений). |
| Кодогенерация | В большинстве языков создаётся enum-case (например, MethodOneofCase в C#/Java) и setter/getter методы (setUsername(), getEmail() и т.п.). |
| Очистка | Установка нового поля внутри oneof автоматически сбрасывает предыдущее. |
| JSON | Сериализуется как обычные поля. При десериализации: последнее установленное значение побеждает (если несколько — поведение неопределено). |
| proto2/proto3 | Поддерживается в обеих версиях. |
Опции oneof (редко используются, но доступны)
- Нет официальных field/message options для
oneofнапрямую. - Можно применить custom options к отдельным полям внутри
oneof.
Пример использования в коде (C# с Google.Protobuf)
var req = new LoginRequest { Email = "user@example.com" };
Console.WriteLine(req.MethodCase); // LoginRequest.MethodOneofCase.Email
req.Username = "admin";
Console.WriteLine(req.MethodCase); // LoginRequest.MethodOneofCase.Username
Разбор:
- Пространства имён группируют модели и сервисы;
async/awaitне блокируют поток при HTTP-вызове наружу.
4.2. map<K, V>
Ассоциативный массив. Компилируется в хеш-таблицы в целевых языках.
Синтаксис
map<string, string> metadata = 1;
map<int32, User> user_cache = 2;
map<string, google.protobuf.Value> dynamic_data = 3;
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
Ограничения
| Параметр | Требования |
|---|---|
K (ключ) | Только string, int32, int64, uint32, uint64, sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool. Не float, double, bytes, message, enum. |
V (значение) | Любые типы, включая message, enum, oneof-содержащие сообщения. Но не map, repeated, group. |
| Field number | Обычное поле. Занимает один номер. Внутренне реализован как repeated message с полями key, value. |
Wire format
Внутренне эквивалент:
message MapFieldEntry {
option map_entry = true;
optional K key = 1;
optional V value = 2;
}
repeated MapFieldEntry map_field = N;
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
🔹
map_entry = true— служебная опция, задаётся автоматически компилятором.
Поведение
| Аспект | Комментарий |
|---|---|
| Порядок | Не гарантируется (как и в хеш-таблицах). |
| Повторяющиеся ключи | При парсинге: последнее значение побеждает. |
| Производительность | map эффективнее repeated KeyValue, но менее гибок (нельзя добавить third-party metadata к записи). |
| Совместимость | Можно безопасно изменить тип поля с repeated KeyValue → map, если структура KeyValue соответствует key/value. Обратное — нет. |
| proto2/proto3 | Доступен только в proto3+. В proto2 — только через repeated message с map_entry=true. |
4.3. repeated
Список/массив значений.
Синтаксис
repeated string tags = 1;
repeated User friends = 2;
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
Опции
| Опция | Тип | Значение по умолчанию (proto3) | Примечание |
|---|---|---|---|
packed | bool | true для скалярных типов | Влияет на wire format: упаковка в один length-delimited chunk (экономия места). Для message/enum — всегда false (нельзя). Устаревшая, но допустимая конструкция: [packed = false]. |
Особенности
| Аспект | Подробности |
|---|---|
| Пустой список | Не передаётся по сети (экономия bandwidth). При десериализации — пустая коллекция (не null). |
| Null-элементы | Недопустимы. Все элементы не-nullable. |
| Производительность | repeated message — эффективнее, чем map при известном ключе (если ключ — индекс или enum). |
| Обратная совместимость | Безопасно: добавление repeated поля не ломает старые клиенты (игнорируют). Удаление — тоже безопасно, если поле не критично. |
5. enum
Перечисления.
Базовый синтаксис
enum StatusCode {
STATUS_UNKNOWN = 0;
STATUS_OK = 200;
STATUS_NOT_FOUND = 404;
}
message Response {
StatusCode code = 1;
}
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
Правила и ограничения
| Правило | Значение |
|---|---|
| Первое значение | Должно быть = 0. В proto3 — это значение по умолчанию для неинициализированных полей. В proto2 — допустимо указать option allow_alias = true, чтобы разрешить дубли значений (но не имён). |
| Значения | 32-битные signed int. Диапазон: −2³¹ … 2³¹−1. |
| Имена | UPPER_SNAKE_CASE по convention. |
| Расширения | В proto2: extend StatusCode { ... } (deprecated). В proto3 — нельзя. |
| Reserved | Можно резервировать значения/имена: reserved 10, 11 to 15; reserved "OLD_CODE"; |
Опции enum и enum-value
| Уровень | Опция | Тип |
|---|---|---|
| enum-level | allow_alias | bool (только proto2) |
| enum-level | deprecated | bool |
| value-level | deprecated | bool |
| value-level | option (my_options).weight = 5; | custom option |
Совместимость
- Добавление нового значения enum — безопасно (старые клиенты получат неизвестное значение → в proto3:
UNRECOGNIZED, в proto2:UNKNOWN_ENUM_VALUE_StatusCode_999). - Удаление значения — небезопасно, если оно использовалось в сериализованных данных.
- Изменение числового значения — категорически запрещено.
JSON mapping
| Поле | Значение | JSON |
|---|---|---|
code = STATUS_OK | 200 | "STATUS_OK" (по имени) или 200 (по числу), в зависимости от настроек сериализатора. |
| Неизвестное значение (999) | — | "UNKNOWN_STATUS_CODE_999" (или число, если enum_as_int). |
🔹 В gRPC-JSON трансляторах (например, grpc-gateway) по умолчанию используется имя, а не число.
6. reserved
Запрещает использование определённых имён или номеров полей/значений enum — для предотвращения несовместимых изменений.
Применение
message User {
reserved 2, 15, 9 to 11;
reserved "login", "password_hash";
string email = 1;
// int32 age = 2; ← ошибка компиляции: номер 2 зарезервирован
// string login = 3; ← ошибка: имя "login" зарезервировано
}
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
Где можно использовать
| Контекст | Синтаксис | Пример |
|---|---|---|
message | reserved <field_number>, <range>, "<name>"; | reserved 4, 5 to 7, "old_field"; |
enum | reserved <value>, <range>, "<name>"; | reserved 500, 501 to 599, "DEPRECATED_CODE"; |
Важно
- Нельзя резервировать пересекающиеся диапазоны.
- Резервирование не влияет на wire format — только на этапе компиляции
.proto. - Необратимо: однажды зарезервированное нельзя "разрезервировать" без поломки совместимости.
7. Extensions (только proto2, deprecated)
Механизм для расширения сообщений без изменения их исходного определения.
Синтаксис (proto2)
// base.proto
message BaseMessage {
extensions 100 to 199;
}
// ext.proto
extend BaseMessage {
optional string extended_field = 101;
}
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
Использование в коде (Java, proto2)
BaseMessage msg = BaseMessage.newBuilder()
.setExtension(Ext.extendedField, "value")
.build();
String val = msg.getExtension(Ext.extendedField);
Разбор:
- Пакеты
model,repository,serviceразделяют слои — сущность, доступ к данным, бизнес-логика обработки заказов.
Почему deprecated
- Нарушает читаемость: структура сообщения не видна из
.proto. - Плохо масштабируется.
- Не поддерживается в proto3 (кроме
Any, см. ниже). - Не поддерживается большинством современных фреймворков (например, gRPC Gateway, Envoy, Linkerd).
✅ Альтернатива:
google.protobuf.Any(см. раздел Well-Known Types).
8. Well-Known Types (google.protobuf.*)
Стандартные типы, поставляемые с protobuf. Импортируются как:
import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";
// ...
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
| Тип | Описание | Пример использования |
|---|---|---|
Any | Упаковка любого сообщения по type_url (например, type.googleapis.com/example.v1.User). | Полиморфные API, события, generic payload. |
Timestamp | Точное время с точностью до наносекунд (RFC 3339). | created_at, last_modified. |
Duration | Продолжительность (например, 3.5s, 2d). | Таймауты, ttl, интервалы. |
Struct, Value, ListValue, NullValue | Динамические JSON-подобные структуры. | Конфиги, метаданные, API с динамической схемой. |
FieldMask | Частичное обновление (update_mask в UpdateRequest). | PATCH-операции, selective fetch. |
Empty | Пустое сообщение (аналог void). | Ping(), Delete() без входных данных. |
DoubleValue, FloatValue, Int64Value, UInt32Value, ... (wrapper types) | Обёртки для nullable скаляров (optional до proto 3.12). | repeated google.protobuf.Int32Value items; где null допустим. |
Api, Method, Syntax, SourceContext | Описание API (для инструментов). | Интроспекция, генерация документации. |
Type, Enum, EnumValue, Option | Метаописание protobuf-схемы. | Рефлексия, динамический парсинг. |
Пример — Any
import "google/protobuf/any.proto";
message Event {
string event_id = 1;
google.protobuf.Any payload = 2;
}
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
В коде (C#):
var user = new User { Id = "u1" };
var any = Any.Pack(user); // type_url = "type.googleapis.com/example.v1.User"
var evt = new Event { Payload = any };
// Распаковка
if (evt.Payload.Is(User.Descriptor)) {
var unpacked = evt.Payload.Unpack<User>();
}
Разбор:
- Пространства имён группируют модели и сервисы;
async/awaitне блокируют поток при HTTP-вызове наружу.
⚠️
Anyтребует, чтобы получатель знал схему упакованного типа (или имелDescriptorPool). Не подходит для полностью динамических систем без реестра типов.
9. Сервисы и методы
9.1. Объявление сервиса
Код ITЗагрузка примера кода…
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
9.2. Типы методов
| Тип | Сигнатура | Назначение | Примеры |
|---|---|---|---|
| Unary | rpc Method(Request) returns (Response) | Один запрос → один ответ. | GetUser, CreateOrder, Ping |
| Server-Streaming | rpc Method(Request) returns (stream Response) | Один запрос → поток ответов. | WatchChanges, SubscribeToNotifications, ListLogEntries |
| Client-Streaming | rpc Method(stream Request) returns (Response) | Поток запросов → один ответ. | UploadFile, BatchInsert, CollectMetrics |
| Bidirectional Streaming | rpc Method(stream Request) returns (stream Response) | Полный duplex. | Chat, CollaborativeEditing, RealtimeAnalytics |
Особенности работы со streaming
- Все streaming-методы требуют явного управления потоком (backpressure, cancellation).
- Протокол — HTTP/2, frame-based (
ДАННЫЕ,HEADERS,RST_STREAM). - Сервер обязан завершать поток вызовом
SendAndClose(unary) илиSend/CloseSend+Recvв цикле (streaming). - Клиент может отменить вызов через
CancellationToken(C#),Context(Go),AbortController(JS/TS).
10. Опции методов и сервисов
10.1. Стандартные и широко используемые опции
| Опция | Уровень | Контекст | Описание |
|---|---|---|---|
(google.api.http) | method | annotations.proto | Сопоставление gRPC ↔ HTTP/JSON (REST-шлюз). Поддерживает get, post, put, patch, delete, body, additional_bindings. |
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) | method | protoc-gen-openapiv2 | Переопределения для OpenAPI/Swagger (summary, description, tags, parameters, responses). |
(google.api.method_signature) | method | annotations.proto | Набор полей, формирующих "сигнатуру" (для генерации методов в SDK, напр., get_user(user_id) вместо get_user(request)). |
(google.api.resource_definition) / (google.api.resource) | file/message | resource.proto | Описание иерархии ресурсов (например, projects/{project}/locations/{location}/clusters/{cluster}). |
(google.api.field_behavior) | field | field_behavior.proto | Семантика поля: REQUIRED, OUTPUT_ONLY, INPUT_ONLY, IMMUTABLE, OPTIONAL. Используется валидаторами и генераторами документации. |
(grpc.method_idempotency_level) | method | gRPC core | IDEMPOTENT / NO_SIDE_EFFECTS / UNKNOWN. Влияет на retry-поведение (если IDEMPOTENT, клиент может повторить при UNAVAILABLE). |
(validate.rules) | field/message | protoc-gen-validate | Правила валидации: string.min_len, int.gt, email, ipv4, uuid, contains, in, pattern и др. |
Пример — HTTP mapping + валидация + OpenAPI
Код ITЗагрузка примера кода…
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
11. Соглашения по именованию и структуре (AIP, Google API Style Guide)
⚠️ Не являются частью протокола, но критически важны для совместимости, документации и инструментария.
11.1. Имена пакетов и файлов
| Элемент | Рекомендация | Пример |
|---|---|---|
package | lowercase, . как разделитель, версия в конце (v1, v1alpha1, v1beta1) | example.user.v1 |
| Файл | lower_snake_case, .proto, группа по домену/версии | user_service.proto, user_resource.proto |
| Импорт | Организован по слоям: стандартные → well-known → внешние → локальные | google/... → third_party/... → example/... |
11.2. Имена сообщений и полей
| Элемент | Рекомендация | Примечание |
|---|---|---|
| Сообщение | UpperCamelCase, существительное или прилагательное + Request/Response/Metadata | CreateUserRequest, ListUsersResponse, User |
| Поле | snake_case, кратко и однозначно | user_id, created_at, display_name |
oneof | snake_case, отражает семантику | contact_info, auth_method |
| Enum | UPPER_SNAKE_CASE, префикс с именем enum (избегать конфликтов) | STATUS_OK, CODE_INVALID_ARGUMENT |
11.3. Стандартные сообщения (AIP-131/132/133/134/135)
| Операция | Рекомендуемое имя метода | Request | Response |
|---|---|---|---|
| Get | Get{Resource} | {Resource}Name → string name | {Resource} |
| List | List{Resources} | string parent, int32 page_size, string page_token | repeated {Resource} {resources}, string next_page_token |
| Create | Create{Resource} | string parent, {Resource} {resource}, string {resource}_id | {Resource} |
| Update | Update{Resource} | {Resource} {resource}, FieldMask update_mask | {Resource} |
| Delete | Delete{Resource} | string name | google.protobuf.Empty |
| Повторная отправка (replay-safe) | Добавить string request_id в Request | — | — |
🔹
name— каноническое имя ресурса:projects/123/users/456.
12. Кастомные опции (custom options)
Позволяют расширять .proto схемы аннотациями для генераторов, линтеров, runtime.
12.1. Объявление кастомной опции
// my_options.proto
syntax = "proto3";
import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions {
string my_field_tag = 50001;
}
extend google.protobuf.MethodOptions {
bool my_method_audit = 50002;
}
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
12.2. Использование
import "my_options.proto";
message User {
string email = 1 [(my_field_tag) = "PII"];
}
service UserService {
rpc GetUser(GetUserRequest) returns (User) {
option (my_method_audit) = true;
}
}
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
12.3. Практическое применение
| Цель | Инструмент | Пример опций |
|---|---|---|
| Валидация | protoc-gen-validate | (validate.rules).string.email = true |
| OpenAPI | protoc-gen-openapiv2 | (grpc.gateway...) |
| Авторизация | Custom middleware | (my.auth).roles = ["admin", "support"] |
| Логгирование | Custom interceptor | (my.log).level = "INFO" |
| Идемпотентность | Client generator | (my.idempotency).key_field = "request_id" |
| Метрики | Observability SDK | (my.metrics).name = "user_get_latency" |
⚠️ Номера расширений (50001+) должны быть уникальны в рамках проекта. Рекомендуется регистрировать в центральном реестре (например,
internal/api-options.proto).
13. Эволюция схемы — правила обратной и прямой совместимости
13.1. Безопасные изменения (forward & backward compatible)
| Изменение | proto2 | proto3 | Примечание |
|---|---|---|---|
Добавить optional/repeated поле | ✅ | ✅ | Старые клиенты игнорируют. Новые — получают default/empty. |
Добавить oneof-поле | ✅ | ✅ | Старые клиенты видят 0/"". |
Добавить map | ✅ | ✅ | Внутренне — repeated message. |
| Добавить enum-значение | ✅* | ✅* | * — клиенты должны обрабатывать UNRECOGNIZED. |
Изменить optional → repeated | ❌ | ⚠️ | Только если поле не использовалось ранее. |
Добавить reserved | ✅ | ✅ | Только для будущего использования. |
13.2. Небезопасные изменения (ломают совместимость)
| Изменение | Последствие |
|---|---|
| Изменить номер поля | Неправильная десериализация (поле A читается как B). |
Изменить тип поля (например, string → int32) | InvalidProtocolBufferException, UnmarshallingError. |
Удалить required поле (proto2) | Крах парсинга у клиентов, отправляющих старый формат. |
| Переименовать enum-значение без резервирования старого | Старые клиенты отправляют старое имя → UNKNOWN. |
13.3. Стратегии миграции
- Двойная запись / двойное чтение (dual writing / dual reading).
- Версионирование API —
v1,v1beta1,v2. FieldMask: клиенты явно указывают, какие поля хотят читать/писать.oneofдля альтернативных форматов:
message Request {
oneof version {
LegacyData v1 = 1;
NewData v2 = 2;
}
}
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
14. Обработка ошибок
14.1. Стандартный статус gRPC (grpc.Status)
| Код (int) | Имя | HTTP-маппинг | Когда использовать |
|---|---|---|---|
0 | OK | 200 | Успех. |
1 | CANCELLED | 499 | Клиент отменил. |
2 | UNKNOWN | 500 | Неизвестная ошибка (логгировать!). |
3 | INVALID_ARGUMENT | 400 | Некорректный запрос (валидация). |
4 | DEADLINE_EXCEEDED | 504 | Таймаут. |
5 | NOT_FOUND | 404 | Ресурс не найден. |
6 | ALREADY_EXISTS | 409 | Конфликт уникальности. |
7 | PERMISSION_DENIED | 403 | Нет прав (не аутентификация!). |
8 | RESOURCE_EXHAUSTED | 429 | Лимиты (rate, quota, memory). |
9 | FAILED_PRECONDITION | 400 | Нарушено предусловие (например, update_mask без полей). |
10 | ABORTED | 409 | Конфликт транзакции (CAS failed). |
11 | OUT_OF_RANGE | 400 | Выход за границы (например, page_size > 1000). |
12 | UNIMPLEMENTED | 501 | Метод не реализован. |
13 | INTERNAL | 500 | Ошибка сервера (баг). |
14 | UNAVAILABLE | 503 | Сервис недоступен (retryable). |
15 | DATA_LOSS | 500 | Потеря данных (редко). |
16 | UNAUTHENTICATED | 401 | Не аутентифицирован. |
14.2. Детализация ошибки — google.rpc.Status
message Status {
int32 code = 1;
string message = 2;
repeated google.protobuf.Any details = 3;
}
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
Пример details:
google.rpc.BadRequest— указание на конкретное поле.google.rpc.QuotaFailure— какая квота исчерпана.google.rpc.PreconditionFailure— какие условия нарушены.- Кастомное сообщение — для внутренних нужд.
14.3. Передача ошибок в клиентах
- C#:
RpcException.StatusCode,RpcException.Trailers,RpcException.Status.Details. - Go:
status.FromError(err),st.Details(). - Java:
StatusRuntimeException.getStatus(). - Python:
grpc.RpcError.code(),details.
15. Кодогенерация — инструменты, плагины и стратегии
15.1. Базовый инструментарий
| Инструмент | Назначение | Версия | Особенности |
|---|---|---|---|
protoc | Официальный компилятор Protocol Buffers | ≥ v3.0 (proto3), ≥ v21.0 (proto3+/edition2023) | Требует явной установки protoc-gen-* плагинов. Не имеет встроенного управления зависимостями. |
buf | Универсальный инструмент для работы с protobuf (lint, format, generate, breaking, mod) | ≥ v1.0 | Включает встроенный protoc, управление модулями, CI-интеграция, строгая проверка совместимости. |
protoc-gen-* | Плагины для генерации кода | Язык-зависимые | Официальные: grpc_***_plugin (C++, Java, Python, Go, Ruby, C#, PHP, Dart, Node.js). |
Минимальная команда protoc
protoc \
--proto_path=src/proto \
--csharp_out=gen/csharp \
--csharp_opt=base_namespace=Example.V1 \
--grpc_out=gen/csharp \
--plugin=protoc-gen-grpc=/usr/local/bin/grpc_csharp_plugin \
src/proto/user_service.proto
Разбор:
protocгенерирует C#/Go/Java-код из.proto; флаги--csharp_outи--grpc_outдобавляют stubs для gRPC.
Эквивалент в buf.gen.yaml
version: v1
plugins:
- name: csharp
out: gen/csharp
opt:
- base_namespace=Example.V1
- name: grpc_csharp
out: gen/csharp
path: grpc_csharp_plugin
Разбор:
- YAML чувствителен к отступам: каждый уровень вложенности — два пробела; ключи слева определяют структуру конфигурации.
→ buf generate
15.2. Популярные генераторы (альтернативы официальным)
| Генератор | Язык | Плюсы | Минусы | Совместимость |
|---|---|---|---|---|
gRPC-Web (protoc-gen-grpc-web) | TS/JS | Работает в браузере через HTTP/1.1 (proxy required) | Требует envoy/gateway, нет streaming в браузере (только unary) | Совместим с gRPC-серверами |
connect-go / connect-web (Buf) | Go/TS | Совместимость с gRPC и Connect (Protocol + JSON), поддержка streaming в браузере (via WebSockets или HTTP/2), меньше boilerplate | Менее "каноничный" для чистого gRPC | Частично обратно совместим |
betterproto (Python) | Python | Typed dataclasses, async-first, меньше boilerplate | Не 100% совместим с grpcio API | Совместим на wire level |
ts-proto | TypeScript | Поддержка NestJS, TypeORM, strict types, enums as union types | Требует отдельной настройки для сервера | Совместим на wire level |
protoc-gen-validate | Мультиязыковой | Runtime validation (Go, Java, Python, JS, C++) | Добавляет зависимость, накладные расходы на валидацию | Является extension, не заменяет генератор |
🔹 Рекомендация: для production-систем — официальные генераторы (
google.golang.org/protobuf,Grpc.Toolsв .NET,grpc-java). Альтернативы — при особых требованиях (браузер, строгая типизация).
15.3. Контроль версий схем — buf.lock и deps
buf поддерживает модули с фиксированными хешами:
// buf.yaml
version: v1
name: buf.build/example/user
deps:
- buf.build/googleapis/googleapis
- buf.build/grpc-ecosystem/grpc-gateway
breaking:
use:
- FILE
lint:
use:
- DEFAULT
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
→ buf mod update фиксирует buf.lock с SHA256-хешами зависимостей.
Это гарантирует повторяемость генерации — все разработчики и CI используют одинаковые версии googleapis, protoc, и т.д.
16. Транспорт и wire format
16.1. Форматы сериализации
| Формат | MIME-Type | Поддержка | Примечания |
|---|---|---|---|
| Binary (proto) | — | Все реализации | Компактный, быстрый, binary-safe. Content-Type: application/grpc (подразумевает binary protobuf). |
| JSON | application/json | grpc-gateway, connect, envoy | Требует google/api/http.proto, FieldMask → updateMask (snake_case). 64-bit → строки по умолчанию. |
| TextFormat | — | Инструменты (protoc --encode), отладка | Человекочитаемый, не для продакшена. Пример: user_id: "u1" created_at: { seconds: 1732000000 }. |
16.2. Сжатие
| Алгоритм | HTTP-заголовок | Поддержка | Накладные расходы |
|---|---|---|---|
gzip | grpc-encoding: gzip, grpc-accept-encoding: gzip | Почти все (C++, Go, Java, .NET) | ЦП: ~10–20% CPU, выигрыш 60–80% размера. |
deflate | grpc-encoding: deflate | Частичная | Менее эффективен, чем gzip. |
snappy | grpc-encoding: snappy | Go, C++, Java (часто custom) | Быстрый, но меньшая степень сжатия (~20–30%). |
| Отсутствие | — | По умолчанию | Для маленьких сообщений (<1 KB) — предпочтительно. |
⚠️ Сжатие не применяется к streaming-фреймам по отдельности — оно действует на весь поток. Сервер и клиент должны согласовать алгоритм через
grpc-accept-encoding.
16.3. HTTP/2 и multiplexing
- gRPC обязательно требует HTTP/2.
- Один TCP-соединение → множество параллельных вызовов (streams).
- Управление потоком:
SETTINGS_INITIAL_WINDOW_SIZE,WINDOW_UPDATE. - Keepalive: настраивается через
grpc.keepalive_time_ms,grpc.http2.max_pings_without_data.
Keepalive параметры (клиент и сервер)
| Параметр | Значение по умолчанию | Рекомендация prod |
|---|---|---|
GRPC_ARG_KEEPALIVE_TIME_MS | INT_MAX (отключено) | 300_000 (5 мин) |
GRPC_ARG_KEEPALIVE_TIMEOUT_MS | 20_000 | 20_000 |
GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS | 0 | 1 (если нужен health-check без вызовов) |
GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA | 2 | 0 (ping только при активности) |
🔹 Без keepalive: промежуточные прокси (nginx, ALB) могут разрывать "тихое" соединение через 60–300 сек.
17. Безопасность
17.1. Транспортный уровень
| Механизм | Описание | Поддержка |
|---|---|---|
| TLS | Шифрование канала | Все реализации. Сертификат сервера обязателен. |
| mTLS | Двусторонняя аутентификация (клиент + сервер) | Требует ssl_client_cert, ssl_client_key у клиента. |
| ALTS | Application Layer Transport Security (Google Cloud only) | grpc++_alts (C++), io.grpc.alts (Java). Не для публичных API. |
Пример инициализации сервера (C#)
var cert = X509Certificate2.CreateFromPemFile("cert.pem", "key.pem");
var keyCertPair = new KeyCertificatePair(cert.Export(X509ContentType.Pem), cert.Export(X509ContentType.Pem));
var server = new Server
{
Services = { UserService.BindService(new UserServiceImpl()) },
Ports = { new ServerPort("0.0.0.0", 50051, new SslServerCredentials(new[] { keyCertPair })) }
};
Разбор:
- Пространства имён группируют модели и сервисы;
async/awaitне блокируют поток при HTTP-вызове наружу.
17.2. Аутентификация и авторизация
| Уровень | Метод | Реализация |
|---|---|---|
| Транспорт | mTLS, TLS client cert | SslClientCredentials + custom interceptor для проверки CommonName/Subject. |
| Метаданные | Authorization: Bearer <token> | Перехват Context.RequestHeaders в interceptor. |
| JWT | Authorization: Bearer <JWT> | Проверка подписи, issuer, exp, scope/roles в claims. |
| API Key | x-api-key: <key> | Проверка в interceptor или gateway. |
Interceptor (middleware) — пример (Go)
func AuthInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Error(codes.Unauthenticated, "no metadata")
}
tokens := md["authorization"]
if len(tokens) == 0 {
return nil, status.Error(codes.Unauthenticated, "missing token")
}
// validate token → ctx = context.WithValue(ctx, "user", user)
return handler(ctx, req)
}
Разбор:
- Пакет
mainсобирается в один бинарник; импорты из стандартной библиотеки не требуют внешних зависимостей.
17.3. Шифрование данных (application-level)
| Требование | Решение |
|---|---|
| PII в логах | Не логировать metadata, request, response напрямую. Использовать маскировку (email → u***@e***.com). |
| Данные в базе | Шифрование на уровне приложения (AES-GCM) до сериализации в protobuf. |
bytes-поля | Могут содержать зашифрованные payload (например, encrypted_payload: bytes). |
⚠️ Protobuf не предоставляет встроенной защиты данных. Шифрование — ответственность приложения.
18. Тестирование и отладка
18.1. Инструменты командной строки
| Инструмент | Назначение | Пример |
|---|---|---|
grpcurl | curl для gRPC | grpcurl -plaintext localhost:50051 list grpcurl -d '{"user_id":"u1"}' localhost:50051 UserService.GetUser |
ghz | Нагрузочное тестирование | ghz --proto user_service.proto --call UserService.GetUser --insecure localhost:50051 |
bloomrpc | GUI-клиент (устаревает) | — |
grpcui | Веб-интерфейс (на основе protoc reflection) | grpcui -plaintext localhost:50051 |
18.2. Reflection и introspection
Сервер может поддерживать сервис grpc.reflection.v1alpha.ServerReflection:
service ServerReflection {
rpc ServerReflectionInfo(stream ServerReflectionRequest) returns (stream ServerReflectionResponse);
}
Разбор:
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе.
→ Позволяет клиентам динамически получать:
- Список сервисов;
- Описание методов;
.proto-файлы (если загружены вFileDescriptorSet).
Включение в Go:
import "google.golang.org/grpc/reflection"
reflection.Register(s)
Разбор:
- Пакет
mainсобирается в один бинарник; импорты из стандартной библиотеки не требуют внешних зависимостей.
В .NET:
services.AddGrpcReflection(); // в Startup
app.MapGrpcReflectionService(); // в Configure
Разбор:
- Пространства имён группируют модели и сервисы;
async/awaitне блокируют поток при HTTP-вызове наружу.
18.3. Unit/integration тестирование
| Язык | Подход |
|---|---|
| C# | Grpc.AspNetCore.Server.ClientFactory, Grpc.Net.Client, TestServer + GrpcChannel.ForAddress(server.BaseAddress) |
| Go | bufconn, grpc.NewServer(), grpc.DialContext(ctx, "bufnet", ...)) |
| Java | InProcessServerBuilder, InProcessChannelBuilder |
| Python | grpc.insecure_channel("localhost:0"), server.add_insecure_port("[::]:0") |
Пример (C#)
Код ITЗагрузка примера кода…
Разбор:
AddHostedService<NotificationConsumer>()регистрирует слушателя очереди в DI;MapControllers()подключает REST-эндпоинты.- Пространства имён группируют модели и сервисы;
async/awaitне блокируют поток при HTTP-вызове наружу.
19. Производительность и тюнинг
19.1. Настройки вызова (CallOptions / CallCredentials)
| Параметр | Уровень | Примечание |
|---|---|---|
| Deadline / Timeout | Клиент | withDeadlineAfter(5, SECONDS) (Java), deadline: DateTime.UtcNow.AddSeconds(5) (C#). Сервер получает Context.Deadline. Превышение → DEADLINE_EXCEEDED. |
| Cancellation | Клиент | CancellationToken (C#), Context.WithCancel() (Go). Сервер получает Context.IsCancellationRequested. |
| Compression | Клиент/Сервер | CallOptions.WithCompression(Compression.Gzip). Сервер должен поддерживать. |
| WaitForReady | Клиент | CallOptions.WithWaitForReady(true). Не завершать вызов при UNAVAILABLE (например, при старте сервера). Использовать осторожно — может привести к зависанию. |
| MaxInboundMessageSize | Сервер/Клиент | MaxReceiveMessageSize (C#), MaxInboundMessageSize (Go). Защита от OOM. По умолчанию: 4 MB. |
| MaxOutboundMessageSize | Клиент/Сервер | Аналогично. |
19.2. Streaming — управление потоком и batching
| Проблема | Решение |
|---|---|
| Backpressure | Использовать Send/Recv с await (async) или SendAsync (C# streaming). Не отправлять быстрее, чем клиент читает. |
| Chunking | Для больших данных (файлы): фиксированный размер чанка (например, 64 KB). Избегать repeated bytes → использовать stream Chunk. |
| Batching ответов | На сервере: буферизовать N элементов или T мс, затем Send(). Снижает overhead фреймов. |
| Flow control (HTTP/2) | Настройка initial_window_size, max_concurrent_streams на стороне сервера (nginx: http2_max_concurrent_streams, http2_chunk_size). |
19.3. Пулы и ресурсы
| Компонент | Рекомендации |
|---|---|
| gRPC-канал (Channel) | Создавать один канал на endpoint и переиспользовать. Дорогая инициализация (TLS handshake, HTTP/2 setup). В .NET: GrpcChannel.ForAddress(...) — singleton. |
| Серверные потоки | Настройка SynchronizationContext (C#), runtime.GOMAXPROCS (Go), grpc.nettySharedGroups (Java). |
| Сериализация | Кэшировать Parser<T> (Java), MessageParser<T> (C#). Избегать повторного парсинга Any. |
| Reflection | Отключать в продакшене (reflection.Register только в dev/staging). |
🔹 Пример пула каналов (C#):
services.AddSingleton<GrpcChannel>(sp =>
GrpcChannel.ForAddress("https://api.example.com", new GrpcChannelOptions
{
Credentials = SslCredentials.Default,
MaxReceiveMessageSize = 100 * 1024 * 1024, // 100 MB
MaxSendMessageSize = 100 * 1024 * 1024
}));
Разбор:
- Пространства имён группируют модели и сервисы;
async/awaitне блокируют поток при HTTP-вызове наружу.
20. Наблюдаемость (Observability)
20.1. Tracing (OpenTelemetry)
gRPC интегрируется с OpenTelemetry через interceptor.
| Язык | Пакет | Примечание |
|---|---|---|
| C# | OpenTelemetry.Instrumentation.GrpcNetClient, Grpc.AspNetCore.Server | Автоматически трассирует unary-вызовы. Streaming — частично. |
| Go | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc | Unary и streaming. |
| Java | io.opentelemetry.instrumentation:opentelemetry-grpc-1.6 | Поддержка gRPC ≥1.6. |
Данные трейса
- Span name:
grpc.client.request/grpc.server.request - Атрибуты:
rpc.Система=grpc,rpc.service=UserService,rpc.method=GetUser,net.peer.ip - События:
message sent,message received
20.2. Metrics (Prometheus)
| Метрика | Тип | Описание |
|---|---|---|
grpc_server_started_total | counter | Всего вызовов (по методу, статусу). |
grpc_server_handled_total | counter | Завершённых вызовов. |
grpc_server_msg_received_total | counter | Принятых сообщений (streaming). |
grpc_server_msg_sent_total | counter | Отправленных сообщений. |
grpc_server_handling_seconds | histogram | Время обработки (P50, P95, P99). |
Экспорт в .NET
services.AddOpenTelemetry()
.WithMetrics(builder => builder
.AddMeter("Grpc.AspNetCore.Server")
.AddPrometheusExporter());
app.MapPrometheusScrapingEndpoint("/metrics");
Разбор:
- Пространства имён группируют модели и сервисы;
async/awaitне блокируют поток при HTTP-вызове наружу.
20.3. Логгирование
| Уровень | Что логировать | Формат |
|---|---|---|
| DEBUG | Входящий/исходящий запрос (для отладки) | Structured: { method: "UserService.GetUser", request: {...}, duration_ms: 12 } |
| INFO | Завершённые unary-вызовы | { method, status, duration_ms, user_id } |
| WARN | DEADLINE_EXCEEDED, UNAVAILABLE (не retryable) | { method, status, attempt } |
| ERROR | INTERNAL, UNKNOWN, DATA_LOSS | { method, status, stack_trace } |
⚠️ Никогда не логировать:
metadata(может содержать токены);- поля с
field_behavior = INPUT_ONLY/SENSITIVE;- содержимое
bytes,Any,Structбез маскировки.
21. Развёртывание и инфраструктура
21.1. Прокси и шлюзы
| Компонент | Назначение | Поддержка gRPC |
|---|---|---|
| Envoy | L4/L7 proxy, rate limiting, retries | Полная: transcoding (REST → gRPC), health checks (/grpc.health.v1.Health/Check), stats. |
| grpc-gateway | Генерация REST-прокси из .proto | Unary ↔ REST, streaming → SSE/WebSocket (ограниченно). |
| nginx | Ingress | Поддержка gRPC с 1.13.10+: grpc_pass grpc://backend; |
| Linkerd / Istio | Service mesh | Автоматический mTLS, retries, timeouts, circuit breaking. |
Пример Envoy config (transcoding)
routes:
- match: { prefix: "/v1/users/" }
route: { cluster: user_service }
typed_per_filter_config:
envoy.filters.http.grpc_json_transcoder:
"@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
proto_descriptor: "/Данные/user_service.pb"
services: ["example.v1.UserService"]
print_options: { always_print_primitive_fields: true }
Разбор:
- YAML чувствителен к отступам: каждый уровень вложенности — два пробела; ключи слева определяют структуру конфигурации.
21.2. Health checks
Стандартный сервис grpc.health.v1.Health:
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}
Разбор:
-
syntaxиpackageзадают версию proto и пространство имён;serviceобъявляет RPC-методы;message— структуры полей на проводе. -
Check: unary, для readiness/liveness probes. -
Watch— streaming, для клиентов, отслеживающих состояние.
🔹 Kubernetes:
livenessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:50051"]
readinessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:50051", "-service=UserService"]
Разбор:
- YAML чувствителен к отступам: каждый уровень вложенности — два пробела; ключи слева определяют структуру конфигурации.
21.3. CI/CD и проверка совместимости
| Инструмент | Команда | Эффект |
|---|---|---|
buf breaking | buf breaking --against '.git#branch=main' | Проверяет, не сломана ли совместимость с main. |
buf lint | buf lint | Соблюдение стиля (AIP, custom rules). |
protolock | protolock status | Legacy-альтернатива buf breaking. |
22. Миграция с REST/SOAP и гибридные API
22.1. Стратегии миграции
| Этап | Действие |
|---|---|
| 1. Dual API | Одновременная поддержка REST и gRPC (например, через grpc-gateway). |
| 2. Клиентская либа | SDK с unified API (.GetUser(id) → gRPC или REST по флагу). |
| 3. Внутреннее gRPC | Внешний REST, внутренние вызовы — gRPC. |
| 4. Полный переход | Только gRPC + gRPC-Web для фронтенда. |
22.2. Преимущества гибридного подхода
- Постепенный переход без downtime.
- Возможность оценить latency/bandwidth/нагрузку.
- Поддержка legacy-клиентов (mobile SDK v1).
22.3. Проблемы
- Дублирование логики (валидация, авторизация).
- Сложность поддержки двух схем (OpenAPI +
.proto). - Разный набор фич (например, streaming недоступен в REST).
🔹 Решение: единственный источник истины —
.proto. OpenAPI генерируется из него (protoc-gen-openapiv2).
23. Анти-паттерны и частые ошибки
| Проблема | Последствие | Исправление |
|---|---|---|
required в proto3 | Синтаксическая ошибка | Использовать optional + валидацию. |
| Field number reuse | Нарушение wire-совместимости | Всегда reserved старые номера. |
Отсутствие request_id | Невозможность идемпотентности | Добавить string request_id = 999 во все Request. |
| Большое сообщение (>4 MB) | RESOURCE_EXHAUSTED | Разбивать на stream, увеличивать MaxReceiveMessageSize. |
| Нет keepalive | Разрыв соединений прокси | Настроить GRPC_ARG_KEEPALIVE_TIME_MS. |
| Синхронный streaming | Блокировка потока, низкая пропускная способность | Использовать async/await, backpressure. |
Логгирование metadata | Утечка токенов | Фильтровать authorization, x-api-key в логгере. |
Отсутствие FieldMask в Update | Перезапись всех полей | Требовать update_mask, использовать mergeFrom. |
| Смешивание proto2/proto3 | Неочевидные default-значения | Стандартизировать на proto3 (или edition 2023). |
Ошибки
| Код / ситуация | Что проверить |
|---|---|
| 4xx | URL, метод, тело, заголовки, авторизация |
| 5xx | логи сервера, таймауты, лимиты, зависимости |
| TLS / сертификат | цепочка, SNI, дата, hostname |
Совместимость
| Область | Примечание |
|---|---|
| Версии | актуальные LTS/стабильные релизы gRPC |
| Платформы | официальная матрица поддержки вендора |
| Стандарты | RFC, ISO, спецификация API — см. таблицы выше |
Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.
В подборках
Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:
Справочники — Справочник по SOAP, Справочник по GraphQL, Справочник по Azure Repos Git, Справочник по RabbitMQ, E2E-тесты и CI с Playwright и GitHub Actions, Справочник по Apache Kafka.