Памятка о protobuf(js)
В рамках рабочих задач познакомился с protobuf. Контекст такой: сериализованные сообщения пишутся в kafka-топик, а нам их надо читать и обрабатывать.
В связи с этим интересно, как эффективно и, не менее важно, лаконично работать с этим форматом в typescript + node.
Для интеграции использовалась библиотека protobufjs
Инициализация root
Очевидно, но можно случайно забыть.
Инициализация Root занимает время. Если частый encoding/decoding, нужно инициализировать единожды.
Хранение схемы
Есть два варианта:
- Хранить строкой
- Хранить proto-файл
Плюсы хранения proto-файлом:
- Легко найти в репозитории, явно
Минусы хранения proto-файлом:
- Нужно включать в сборку
Плюсы хранения константой:
- Чуть более производительная инициализация Root
Минусы хранения константой:
- Не поддерживает типы из пакетов, например
google.protobuf.Timestamp
Если инициализация происходит не часто, то хранение файлом выигрывает.
Опция keepCase
Преобразовывает поля из snake_case в схеме protobuf в camelCase.
По умолчанию true.
Плюсы:
- можно не использовать в коде snake_case
На производительность явно не влияет, значение можно выбирать в зависимости от предпочтений.
Опция defaults
Protobuf позволяет не передавать значение для полей при сериализации.
Отсутствие значения = значение по умолчанию.
Defaults в значении true подставляет значения по умолчанию для не опциональных полей, если значение не было передано при сериализации.
Плюсы использования defaults: true:
- Явно. Если значение не optional, то оно не будет
undefinedпре десериализации.
Минусы:
- Менее производительно
По поводу производительности: явность позволяет не добавлять доп логику на проверку наличия полей, что в итоге может дать прирост с ростом бизнес-логики.
Я выбираю явность (defaults: true)
Опция: bytes: String
Добавление такой опции автоматически преобразует значения полей типа bytes в строку.
Проблема в том, что это преобразование в base64, что может оказаться неожиданностью. Лучше самостоятельно проебразовывать такие поля в необходимый тип.
Не использую
Опция: enum: String
Добавление такой опции автоматически преобразует значения полей типа enums в строку. Иначе значение остается int'ом.
На производительность явно не влияет, значение можно выбирать в зависимости от предпочтений.
Итого
import { Root, Type } from "protobufjs";
interface Input {
filePath: string;
type: string;
}
export class ProtobufMessageEncoder {
private readonly type: Type;
constructor({filePath, type}: Input) {
const root = new Root();
root.loadSync(filePath, {keepCase: false});
this.type = root.lookupType(type);
}
public decode(value: Buffer | Uint8Array) {
const message = this.type.decode(value);
return this.type.toObject(message, {defaults: true});
}
}
- Инициализируем один раз
- Явно используем proto-файлы
- Наличие полей в соответствии с protobuf-схемой
- Нет неожиданных значений в bytes
- Нейминг полей и преобразование значений enum по предпочтению