Java: байт Protobuf в строку Json для быстрого Pojo

Я получаю сообщения в формате protobuf. Мне нужно быстро преобразовать его в формат json, так как вся моя бизнес-логика написана для обработки объектов POJO на основе json.

byte[] request = ..; // msg received

// convert to intermediate POJO
AdxOpenRtb.BidRequest bidRequestProto = AdxOpenRtb.BidRequest.parseFrom(request, reg);

// convert intermediate POJO to json string.
// THIS STEP IS VERY SLOW
Printer printer = JsonFormat.printer().printingEnumsAsInts().omittingInsignificantWhitespace();
String jsonBody = printer.print(bidRequestProto);

// convert json string to final POJO format
BidRequest bidRequest = super.parse(jsonBody.getBytes());

Шаг преобразования протообъекта в json очень медленный. Есть ли более быстрый подход для этого?

могу ли я повторно использовать объект printer? это потокобезопасно?

Примечание. Этот класс POJO (AdxOpenRtb.BidRequest и BidRequest) очень сложный, имеет множество иерархий и полей, но содержит аналогичные данные с немного разными именами полей и типами данных.

Вручную напишите код для перевода с одного POJO на другой. (Я предполагаю, что вы не создаете Printer для каждого объекта)

tgdavies 02.01.2023 07:44

@tgdavies Я создаю Printer для каждого объекта. могу ли я восстановить его? (я не нашел ни одного документа, который говорит об этом). Printer printer = JsonFormat.printer().printingEnumsAsInts().omittingInsignifi‌​cantWhitespace();. это используется несколькими потоками. это потокобезопасно?

Nishant Kumar 02.01.2023 08:05

Я не знаю, является ли он потокобезопасным. Я бы предположил, что это так.

tgdavies 02.01.2023 08:21

Не просто копируйте от одного pojo к другому. Не выполняйте промежуточный шаг для JSON, который потребует только ресурсов. Используйте что-то вроде MapStruct для создания картографов.

M. Deinum 02.01.2023 14:12
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
4
92
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я также столкнулся с некоторыми проблемами производительности и в итоге написал библиотеку QuickBuffers. Он генерирует специальные методы сериализации JSON (т.е. без отражения) и должен дать вам ускорение в 10-30 раз. Его можно использовать параллельно с реализацией Google. Код должен выглядеть примерно так:

    // Initialization (objects can be reused if desired)
    AdxOpenRtb.BidRequest bidRequestProto = AdxOpenRtb.BidRequest.newInstance();
    ProtoSource protoSource = ProtoSource.newArraySource();
    JsonSink jsonSink = JsonSink.newInstance().setWriteEnumsAsInts(true);

    // Convert Protobuf to JSON
    bidRequestProto.clearQuick() // or ::parseFrom if you want a new object
            .mergeFrom(protoSource.setInput(request))
            .writeTo(jsonSink.clear());

    // Use the raw json bytes
    RepeatedByte jsonBytes = jsonSink.getBytes();

В JsonSinkBenchmark есть пример кода для замены встроенного кодировщика JSON более проверенными в боевых условиях бэкендами Gson/Jackson.

Обновлено: если вы делаете это в рамках одного процесса и беспокоитесь о производительности, вам лучше написать или сгенерировать код для прямого преобразования объектов Java. JSON — не очень эффективный формат.

Я пытался использовать QuickBuffers, но не смог сгенерировать файлы: github.com/HebiRobotics/QuickBuffers/issues/35

Nishant Kumar 11.01.2023 05:58

В настоящее время типы выделяются с готовностью, чтобы избежать выделения устойчивого состояния, поэтому его нельзя использовать с рекурсивными определениями сообщений, такими как Struct. Я рассматриваю возможность добавления флага для ленивого распределения в будущем, но похоже, что вы все равно не используете это сообщение.

Florian Enner 11.01.2023 15:18
Ответ принят как подходящий

В итоге я использую MapStruct, как предложили некоторые из вас (@M.Deinum).

новый код:

byte[] request = ..; // msg received

// convert to intermediate POJO
AdxOpenRtb.BidRequest bidRequestProto = AdxOpenRtb.BidRequest.parseFrom(request, reg);

// direct conversion from protobuf Pojo to my custom Pojo
BidRequest bidRequest = BidRequestMapper.INSTANCE.adxOpenRtbToBidRequest(bidRequestProto);

Фрагмент кода BidRequestMapper:

@Mapper(
    collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
    unmappedSourcePolicy = ReportingPolicy.WARN, unmappedTargetPolicy = ReportingPolicy.WARN)
@DecoratedWith(BidRequestMapperDecorator.class)
public abstract class BidRequestMapper {

  public static final BidRequestMapper INSTANCE = Mappers.getMapper(BidRequestMapper.class);

  @Mapping(source = "impList", target = "imp")
  @Mapping(target = "impOverride", ignore = true)
  @Mapping(target = "ext", ignore = true)
  public abstract BidRequest adxOpenRtbToBidRequest(AdxOpenRtb.BidRequest adxOpenRtb);
...
...
}

// manage proto extensions
abstract class BidRequestMapperDecorator extends BidRequestMapper {
  private final BidRequestMapper delegate;

  BidRequestMapperDecorator(BidRequestMapper delegate) {
    this.delegate = delegate;
  }

  @Override
  public BidRequest adxOpenRtbToBidRequest(AdxOpenRtb.BidRequest bidRequestProto) {
    // Covert protobuf msg to basic bid request object
    BidRequest bidRequest = delegate.adxOpenRtbToBidRequest(bidRequestProto);
...
...
  }
}

Новый подход работает в 20-50 раз быстрее в моей локальной тестовой среде.

Стоит отметить, что MapStruct — это процессор аннотаций, который делает его намного быстрее, чем другие подобные библиотеки, использующие отражение, а также имеет очень хорошую поддержку настройки.

Другие вопросы по теме