Я получаю сообщения в формате 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) очень сложный, имеет множество иерархий и полей, но содержит аналогичные данные с немного разными именами полей и типами данных.
@tgdavies Я создаю Printer для каждого объекта. могу ли я восстановить его? (я не нашел ни одного документа, который говорит об этом). Printer printer = JsonFormat.printer().printingEnumsAsInts().omittingInsignificantWhitespace();. это используется несколькими потоками. это потокобезопасно?
Я не знаю, является ли он потокобезопасным. Я бы предположил, что это так.
Не просто копируйте от одного pojo к другому. Не выполняйте промежуточный шаг для JSON, который потребует только ресурсов. Используйте что-то вроде MapStruct для создания картографов.




Я также столкнулся с некоторыми проблемами производительности и в итоге написал библиотеку 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
В настоящее время типы выделяются с готовностью, чтобы избежать выделения устойчивого состояния, поэтому его нельзя использовать с рекурсивными определениями сообщений, такими как Struct. Я рассматриваю возможность добавления флага для ленивого распределения в будущем, но похоже, что вы все равно не используете это сообщение.
В итоге я использую 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 — это процессор аннотаций, который делает его намного быстрее, чем другие подобные библиотеки, использующие отражение, а также имеет очень хорошую поддержку настройки.
Вручную напишите код для перевода с одного POJO на другой. (Я предполагаю, что вы не создаете
Printerдля каждого объекта)