Инфраструктура и преамбула
У меня есть приложение PlayFramework (2.3.8), размещенное на экземпляре AWS EC2. У меня есть массив сложных объектов, которые должны быть возвращены в виде строки JSON через веб-API. Мне нужна глубокая копия массива со всеми дочерними объектами, полностью загруженными до самого последнего слоя. Массив имеет размер 30-100 записей, каждая запись имеет около 1-10 записей, каждая запись из которых имеет до 100 свойств, в конце концов, нет никаких BLOB или подобных элементов, все сводится к строкам/двойникам/ целые/логические значения. Я не уверен, насколько важна точная структура данных. Пожалуйста, дайте мне знать, если вам нужна дополнительная информация. Размер результирующего файла .json составляет около 1 МБ.
Производительность десериализации этого массива ужасна, для ~ 1 МБ на моей локальной машине это занимает 3-5 минут; на EC2 это занимает около 20-30 секунд.
Первоначальная проблема: низкая производительность при использовании play.libs json
Мой массив объектов загружается и сохраняется как JsonNode. Затем этот JsonNode перенаправляется в ObjectMapper, который, наконец, записывает его в prettyPrinted:
List<myObject> myObjects = myObjectService.getInstance().getAllObjects(); // simplified example
JsonNode myJsonNode = Json.toJson(myObjects); // this line of code takes a huge amount of time!
ObjectMapper om = new ObjectMapper();
return om.writerWithDefaultPrettyPrinter().writeValueAsString(myJsonNode); // this runs in <10 ms
Итак, я определил виновника десериализации Json.toJson. Насколько мне удалось выяснить, это своего рода обернутая библиотека Джексона, которая используется PlayFramework.
Хотя я читал о некоторых проблемах с производительностью при десериализации JSON, я не уверен, следует ли нам говорить о каких-то сотнях миллисекунд или секундах, а не о минутах. Во всяком случае, я попытался внедрить некоторые другие библиотеки JSON (GSON, argonaut, flexjson), что на самом деле не прошло гладко.
ГСОН
Я «просто» попытался заменить библиотеку play-json на библиотеку GSON, как и в другой небольшой части проекта. Там он работал нормально, но хотя у меня НЕТ циклических ссылок, он выдает StackOverflowErrors прямо мне в лицо, даже если я пытаюсь десериализовать крошечный объект, созданный вручную. Как на моей машине разработки, так и на экземпляре EC2.
FlexJson
List<myObject> myObjects = myObjectService.getInstance().getAllObjects(); // simplified example
JSONSerializer serializer = new JSONSerializer().prettyPrint(true);
return serializer.deepSerialize(myObjects); // returns a prettyPrinted String
До сих пор работало нормально, это занимает всего около 20% времени по сравнению с методом Json.toJson выше. Однако это может быть потому, что он НЕ ДЕЙСТВИТЕЛЬНО глубоко копирует объекты. Он глубоко копирует его на первом слое, однако, поскольку моя модель имеет более сложные свойства (с дочерними элементами, внуками и внуками...), и их довольно много, я не уверен, как действовать здесь.
Вот пример вывода одного из моих вложенных объектов (это одно из свойств «верхнего» объекта):
"class": "com.avaje.ebean.common.BeanList",
"empty": false,
"filterMany": null,
"finishedFetch": true,
"loaderIndex": 0,
"modifyAdditions": null,
"modifyListenMode": "NONE",
"modifyRemovals": null,
"populated": true,
"propertyName": "elements",
"readOnly": false,
"reference": false
Есть ли у вас какие-либо другие предложения по решению или намеки на то, что может быть сломано? Я также думал о том, что, возможно, сущности загружаются ПОЛНОСТЬЮ только после того, как я вызову .toJson()? Тем не менее, это не должно занимать столько времени.
Заранее спасибо!
.toJson()
. Как я вижу, вы используете Ebean. Попробуйте активировать ведение журнала для операторов sql, чтобы узнать, граф объекта загружается до или во время вызоваtoJson
. - person marcospereira   schedule 09.11.2016toJson
(если этот процесс быстрее, чем предыдущий, то явно что-то другое, чем анализ/генерация json, делает его медленнее) - person Salem   schedule 10.11.2016