Инфраструктура и преамбулаPlayFramework: низкая производительность десериализации JSON
У меня есть PlayFramework (2.3.8) приложение, размещенного на экземпляре AWS EC2. У меня есть массив сложных объектов, который должен быть возвращен как строка JSON через веб-API. Мне нужна глубокая копия массива, при этом все дочерние объекты будут полностью загружены до самого последнего слоя. Массив имеет размер 30-100 записей, каждая запись имеет около 1-10 записей, каждая запись которых имеет до 100 свойств, в конце нет BLOB или аналогичных элементов, все это сводится к строкам/удвоениям/Ints/Bools. Я не уверен, насколько важна точная структура данных, пожалуйста, дайте мне знать, если вам нужна дополнительная информация. В результате размер файла .json составляет около 1 МБ.
Производительность десериализации этого массива ужасна, для ~ 1 МБ на моей локальной машине требуется 3-5 минут; на EC2 это занимает около 20-30 секунд.
Исходная задача: низкая производительность при использовании play.libs JSon
Мой массив объектов загружается и сохраняется как JsonNode. Это JsonNode затем направляются к ObjectMapper, который, наконец, пишет он набран аккуратно:
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), которые на самом деле не прошли гладко.
GSON
Я «просто» попытался заменить библиотеку плей-JSon с библиотекой GSON, как я сделал на другой небольшой части проекта. Он отлично работал там, но хотя у меня нет круговых ссылок, он бросает StackOverflowErrors на мое лицо, даже если я пытаюсь десериализовать крошечный созданный вручную объект. Как на моей машине dev, так и на экземпляре 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 выше. Это может быть, однако, потому, что оно не ДЕЙСТВИТЕЛЬНО глубоко копирует объекты. Он глубоко копирует его на первом уровне, однако, поскольку моя модель обладает более сложными свойствами (с дочерьми и внуками и grandgrandchilds ...), и их довольно много, я не уверен, как здесь разбираться.
Вот пример вывода одного из моих вложенных объектов (это одна из свойств «верхнего» объекта):
"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] (http://stackoverflow.com/questions/9371907/where-to-see-the-logged-sql-statements-in-play2), чтобы увидеть, загружен ли граф объекта до или во время вызова 'toJson'. – marcospereira
Также вы можете попробовать использовать что-то вроде VisualVM или YourKit, чтобы узнать, что замедляет работу, или сохранить сгенерированный файл json. загрузите его в память (разберешь его), а затем отрисуйте его с помощью 'toJson' (если этот процесс быстрее предыдущего, то, очевидно, что-то другое, чем json parsing/generation делает его медленнее) – Salem
Спасибо за подсказку с регистрацией sql, good идея. Оказывается, что сущности и их дочерние сущности загружаются только во время Json.toJson(). Я попытался изменить fetch = FetchType.Eager для этих дочерних объектов, но это не имело никакого значения.И в основном я не вижу, какая разница, в какой момент загружаются объекты, так почему бы не во время вывода для .toJson(). Но похоже, что узкое место связано с сущностями/БД, а не с сериализацией JSON ..? –