Protobuf: proto-контракты, типы, эволюция схем
↑ 5.2 gRPC · ← Предыдущая · → Следующая
Зачем это на собесе
Как менять контракт gRPC/Kafka, не ломая клиентов: номера полей, reserved, обратная совместимость.
Объяснение
syntax = "proto3";
option csharp_namespace = "Orders.Contracts";
service OrderService {
rpc GetOrder (GetOrderRequest) returns (OrderReply);
rpc Watch (WatchRequest) returns (stream OrderEvent);
}
message GetOrderRequest { string id = 1; }
message OrderReply {
string id = 1;
string customer = 2;
repeated Item items = 3;
google.protobuf.Timestamp created_at = 4;
Status status = 5;
reserved 6, 7; // удалённые поля
reserved "old_name";
}
enum Status { STATUS_UNSPECIFIED = 0; NEW = 1; PAID = 2; }| Тип proto | C# |
|---|---|
int32/int64, uint32 | int, long, uint |
double, float, bool, string, bytes | одноимённые / ByteString |
repeated T | RepeatedField<T> |
map<K,V> | MapField<K,V> |
google.protobuf.Timestamp/Duration | Timestamp, Duration (конвертация в DateTime) |
oneof | взаимоисключающие поля |
optional | различает «не задано» и значение по умолчанию |
Эволюция схемы
Безопасно: добавлять новые поля с новыми номерами, добавлять значения enum, менять имена полей (по сети идут номера).
Опасно: менять номер или тип поля, переиспользовать удалённый номер, менять repeated/скаляр.
Удаляя поле, помечайте его reserved.
Нюансы и подводные камни
- В proto3 у скаляров нет
null: значение по умолчанию (0, "", false) неотличимо от «не задано» безoptional. - Первое значение enum должно быть
0(UNSPECIFIED). - Деньги не храните в
double:string/int64в минимальных единицах илиMoney-сообщение. - Ошибки контракта лучше ловить проверками совместимости (
buf breaking).
Практика
- Опишите контракт заказов и сгенерируйте C#-код.
- Добавьте поле, затем удалите его с
reserved, проверьте совместимостьbuf breaking. - Используйте
oneofдля разных способов оплаты.
Вопросы с ответами
Почему нельзя менять номера полей?
В бинарном формате поле идентифицируется номером; смена сломает десериализацию у старых клиентов.
Что делает reserved?
Запрещает повторное использование номеров/имён удалённых полей.
Как различить 0 и «не задано» в proto3?
Использовать
optionalили wrapper-типы.