天道酬勤,学无止境

PlayFramework:糟糕的 json 反序列化性能(PlayFramework: poor json deserializing performance)

问题

基础设施和序言

我有一个托管在 AWS EC2 实例上的 PlayFramework (2.3.8) 应用程序。 我有一组复杂的对象,应该通过 Web API 作为 JSON 字符串返回。 我需要一个数组的深层副本,所有子对象都完全加载到最后一层。 该数组的大小为 30-100 个条目,每个条目大约有 1-10 个条目,其中每个条目最多有 100 个属性,最终没有涉及 BLOB 或类似内容,这一切都归结为字符串/双精度/整数/布尔值。 我不确定确切的数据结构有多重要,如果您需要更多详细信息,请告诉我。 生成的 .json 文件大小约为 1 MB。

反序列化这个数组的性能很糟糕,对于我本地机器上的 ~1 MB 需要 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 使用的一种包装好的 Jackson 库。

虽然我已经阅读了 JSON 反序列化的一些性能问题,但我不确定我们是否应该谈论几百毫秒到几秒,而不是几分钟。 无论如何,我尝试实现其他一些 JSON 库(GSON、argonaut、flexjson),但进展并不顺利。

GSON

我“只是”尝试用 GSON 库替换 play-json 库,就像我在项目的另一小部分所做的那样。 它在那里工作得很好,但即使我没有循环引用,它也会向我抛出 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

到目前为止工作得很好,与上面的 Json.toJson 方法相比,它只需要大约 20% 的时间。 然而,这可能是因为它并没有真正深入复制对象。 它确实在第一层进行了深度复制,但是由于我的模型具有一些更复杂的属性(包括子孙和孙子......),而且很多,我不确定如何在这里进行。

这是我的嵌套对象之一的示例输出(这是“上层”对象的属性之一):

 "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() 后才完全加载? 仍然不应该花费这么长的时间。

提前致谢!

回答1

TLDR:这个问题与 PlayFrameworks JSON 反序列化性能无关,与一些 eBean/数据库问题无关。 在 application.conf 中启用 SQL 日志记录向我指出了这一点。


进一步的评论和想法:感谢评论中 marcospereira 的暗示,我将问题归结为 play/ebeans 中的 fetch 问题,而不是 JSON 性能问题。

显然,我的实体一开始是惰性加载的(/flat),通过启用 SQL 日志记录,我可以看到只有在我的代码命中 .toJson() 时才会触发正确准备的 SELECT。 如此多的子对象仅在调用 .toJson() 时才从数据库中获取,这会导致数百个 SELECT,因此需要相当长的时间才能完成。

稍微玩一下 RDS 实例规模,我发现了一些非常奇怪的结果。 这与最初提出的问题并没有真正相关,但我想分享我的发现,也许它可以对那里的人有所帮助。 在下面的部分中阅读它。

RDS 扩容实验...

在我的开发环境 (t1.micro) 中,我将生产数据库的复制实例连接到一个小型 RDS 实例 (db.t2.micro) 上,以查看是否有任何变化。

我的 prod 环境 (t2.large) + prod RDS (db.t2.large) 花了大约 19.5 秒完成 API 调用。 新的开发环境(t1.micro + db.t2.micro)在计算和 db 上都较弱,只花了大约 10.5 秒,这是非常不确定的,因为基本上两个实例运行的代码完全相同,只是指向到另一个数据库服务器(具有相同的数据库内容)。 我将开发数据库切换到 db.m4.large 以查看是否有任何改进,加载时间下降到大约 5.5 秒。

我完全不明白为什么更快的 prod EC2 实例比开发实例需要更多的时间来进行完全相同的 API 调用。 最后,我将 prod db 类从 db.t2.large 更改为 db.m4.large,现在响应时间为 4.0 秒。

感觉像“旧”的 prod 数据库实例有点破旧/堵塞(有这样的事情吗?我有点怀疑......)。 即使是较小的开发实例 + 开发数据库也能更快地响应。 尽管不同的 RDS 缩放带来了一些改进,但我怀疑 db.t2.large -> db.m4.large 之间的差异是否会导致该幅度的变化。

也许如果有人对正在发生的事情有一些想法,我会很乐意讨论这个问题。

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 如何在Play Json中使用Joda DateTime(How to use Joda DateTime with Play Json)
    问题 我正在开发Play应用程序,并且试图在我的case类中使用Joda DateTime对象。 package model import org.joda.time.DateTime import play.api.libs.json._ case class User(name: String, created: DateTime) object User { implicit val yourJodaDateReads = Reads.jodaDateReads("yyyy-MM-dd'T'HH:mm:ss.SSSZ") implicit val yourJodaDateWrites = Writes.jodaDateWrites("yyyy-MM-dd'T'HH:mm:ss.SSSZ'") implicit val userFormat = Json.format[User] def main(args: Array[String]) { val value = Json.parse("{ \"name\" : \"hello\" , \"created\" : \"2015-07-16T20:32:04.046+02:00\" }") println(Json.toJson(new User("user", new DateTime()))) println(Json
  • PlayFramework: poor json deserializing performance
    Infrastructure and preamble I have a PlayFramework (2.3.8) App hosted on an AWS EC2 instance. I have an array of complex objects, which should be returned as a JSON string via a web API. I need a deep copy of the array, with all child objects fully loaded until the very last layer. The array has the size of 30-100 entries, each entry has around 1-10 entries, each entry of those has up to 100 properties, in the end there are no BLOBs or similar involved, it all boils down to strings/doubles/ints/bools. I am unsure how far the exact data structure is of importance, please let me know if you need
  • 从 PlayFramework 表单发布 JSON 数据(Post JSON-Data from PlayFramework form)
    问题 Play Framework 提供了一种通过request().body().asJson()访问请求正文中的 JSON-Data 的方法。 使用表单助手不会以 JSON 格式发布数据。 那么,在播放应用程序中,在将表单数据传递给控制器之前将表单数据转换为 json 对象的最佳方法是什么? 提前致谢。 回答1 当您检索请求有效负载数据时,您可以使用BodyParsers (它们使用Content-Type标头将有效负载解析为其他内容),或者您可以通过表单绑定自己获取有效负载或直接作为 JSON 获取有效负载,如果您有 JSON/文本有效负载请求正文。 在您的情况下,您的Content-Type为application/x-www-form-urlencoded或multipart/form-data 。 因此,您需要使用辅助类绑定到该表单以获取该数据,如果您真的想将其转换为 JSON,您只需添加一个将其插入 ObjectNode 的附加步骤。 如果您希望表单数据为 JSON,请直接在前端进行转换(如果可能)并将其作为Content-Type application/json发送到正文中。 现在,您是否明白为什么您想要做的只是增加了额外的复杂性而没有明显的收益? 回答2 1.将表单序列化为JSON-Object $.fn.serializeObject = function()
  • Json Coast to Coast Play 框架:序列化 Joda DateTime(Json Coast to Coast Play framework : Serializing Joda DateTime)
    问题 大家好,我是玩框架的新手,如果有人知道下面提到的更好的方法,请告诉我。 所以我有一个模型和读取/写入/格式 case class Schedule (startDate: DateTime, endDate: DateTime) object ScheduleSerializers { val userDateFormatter = "dd/MM/yyyy HH:mm:ss" val nonImplicitUserFormatter = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss") implicit val jodaDateTimeReads = Reads.jodaDateReads(userDateFormatter) implicit val jodaDateTimeWrites = Writes.jodaDateWrites(userDateFormatter) implicit val readSchedule: Reads[Schedule] = ( (__ \ "startDate").read[String].map[DateTime](dt => DateTime.parse(dt, nonImplicitUserFormatter)) and (__ \ "endDate").read[String]
  • Gson类型适配器与自定义脱盐器(Gson Type Adapter vs. Custom Deseralizer)
    问题 下面的示例显示了一个类(Club),其中包含抽象类(成员)的集合。 我对于是否需要TypeAdapter或JsonDeserializer才能使反序列化正常工作感到困惑。 序列化在没有任何帮助的情况下就可以正常工作,但是反序列化会引发异常。 为了说明这一点,我构建了以下“克隆”测试。 如果有人能展示一个有效的例子,我将不胜感激。 头等舱 package gson.test; import java.util.ArrayList; import com.google.gson.Gson; public class Club { public static void main(String[] args) { // Setup a Club with 2 members Club myClub = new Club(); myClub.addMember(new Silver()); myClub.addMember(new Gold()); // Serialize to JSON Gson gson = new Gson(); String myJsonClub = gson.toJson(myClub); System.out.println(myJsonClub); // De-Serialize to Club Club myNewClub = gson
  • PlayFramework:如何转换 JSON 数组的每个元素(PlayFramework: how to transform each element of a JSON array)
    问题 鉴于以下 JSON... { "values" : [ "one", "two", "three" ] } ...我如何在 Scala/Play 中像这样转换它? { "values" : [ { "elem": "one" }, { "elem": "two" }, { "elem": "three" } ] } 回答1 您可以使用 Play 的 JSON API: import play.api.libs.json._ val json = Json parse """ { "values" : [ "one", "two", "three" ] } """ val newArray = json \ "values" match { case JsArray(values) => values.map { v => JsObject(Seq("elem" -> v)) } } // or Json.stringify if you don't need readability val str = Json.prettyPrint(JsObject(Seq("values" -> JsArray(newArray)))) 输出: { "values" : [ { "elem" : "one" }, { "elem" : "two" }, { "elem" : "three
  • 如何反序列化包含多维数组的json对象?(How to deseralize json object that contains multidimensional array?)
    问题 我需要一些关于将包含多维数组的 JSON 对象转换为我的类的帮助。 我试图反序列化 json 对象但失败了。 JsonMaclar 类对象为空。 请帮忙。 脚本代码; var allFields = new Array(); allFields.push({ BirinciKatilimciId: birinciKatilimciId.val(), IkinciKatilimciId: ikinciKatilimciId.val(), BirincininSkoru: birincininSkoru.val(), IkincininSkoru: ikincininSkoru.val(), MacSayisi: macSayisi.val(), MacSuresi: macinSuresi.val(), MacinOynanmaSaati: macinOynanmaSaati.val(), Hukmen: hukmen.is(':checked'), RatingeDahil: ratingeDahil.is(':checked'), MaclarTablosundaGoster: maclarTablosundaGoster.is(':checked'), MacinTarihi: macinTarihi.val() }); $("#<%=btnMaclariKaydet
  • 无法从 START_OBJECT 令牌反序列化 org.joda.time.DateTime 或 LocalDate 的实例(Can not deserialize instance of org.joda.time.DateTime or LocalDate out of START_OBJECT token)
    问题 v2.1.1,joda 模块。 我可以使用 objectMapper.readValue(file, pojo .class); 在单元测试中将 json 文件转换为 pojo。 但是,当 Spring RESTTemplate 客户端调用默认的 json 转换器来转换包含具有 Joda 类型(DateTime 或 LocalDate)的域对象的 inputStream 时,它会生成错误: objectMapper.readValue(httpInputMessage.getBody(), javaType) com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of org.joda.time.DateTime out of START_OBJECT token at Source: org.mortbay.jetty.HttpParser$Input@46a09b; line: 1, column: 752 at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164) at com.fasterxml.jackson.databind
  • 如何使用JsonFormat将Jackson Json NULL字符串反序列化为日期(How to deserialize Jackson Json NULL String to Date with JsonFormat)
    问题 我已经看了很多,但到目前为止仍然无法得到答案,非常感谢任何帮助! 我有一个简单的String到Date字段映射,并尝试将 JSON 字符串读取到 Java 对象。 @JsonInclude(value=Include.NON_EMPTY) @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd-MMM-yyyy", timezone="PST") protected Date eolAnnounceDate; 但是,如果 JSON 字符串值为空,我会收到以下异常。 有人可以告诉我如何解决这个问题吗? 我尝试了一些选项,但它们都是用于序列化的。 ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setSerializationInclusion(Include.NON_NULL); objectMapper.setSerializationInclusion(Include.NON_EMPTY); 例外 : java.lang.IllegalArgumentException:无法解析日期值“NULL”(格式:“dd-MMM-yyyy”):无法解析的日期:“NULL”com.fasterxml.jackson.databind.deser.std
  • JACKSON 支持 Java 泛型吗?(JACKSON support for Java Generics?)
    问题 目前,我正在研究一个基于架构的 restFul 项目。 因此,我们使用 JAXB 进行 XSD-->JAVA 转换。 我有一个类如下: @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "systemResponse" }) @XmlRootElement(name = "RestResponse") public class RestResponse implements Serializable { private final static long serialVersionUID = 1L; @XmlElementRef(name = "SystemResponse", namespace = "http://www.intuit.com/psd/cdm/v1", type = JAXBElement.class) protected JAXBElement<? extends CdmComplexBase> systemResponse; ... } 这是它的序列化方式: {"systemResponse":{"name":"{http://www.intuit.com/psd/cdm/v1}Transactions","declaredType":"com.intuit
  • Scala:将 JSON 直接解析为案例类(Scala: Parse JSON directly into a case class)
    问题 给定一串 JSON 和一个与之对应的 case 类,有什么简单的方法可以将 JSON 解析为 case 类? 有许多可用的库,但 Scala 现在似乎可以开箱即用。 如果 JSON 应该被解析为 case 类的列表呢? 更新: Jerkson 似乎被放弃了,我不想安装完整的 Play 或 Lift 框架或其他任何沉重的东西。 回答1 有几个框架可以完全做到这一点。 圆 现在用的很多。 许多很棒的功能。 会拉猫进来。 https://circe.github.io/circe/ https://github.com/circe/circe JSON4s JSON4s 非常成熟,支持jackson或原生JSON-Parser。 在许多项目中使用它来代替 jerkson。 https://github.com/json4s/json4s 播放-json 可以在没有完整播放堆栈的情况下使用。 作为 typesafe 游戏项目的一部分的大力支持。 http://www.playframework.com/documentation/2.0/ScalaJson Scala 酸洗 一个序列化框架。 有一个选项可以序列化/和反序列化为 JSON。 https://github.com/scala/pickling 喷 JSON 可以序列化和反序列化。 需要知道反序列化困难的参数数量。
  • Playframework JSON 解析 - 空指针异常 - 当数组存在时(Playframework JSON parsing - Null pointer exception - when array is present)
    问题 我正在使用 playframework 进行 JSON 解析,并且面临以下 NullPointerException: 我的数据模型如下: case class SearchLikeThisResult(total: Int, max_score: Double, hits: Seq[Hits]) case class Hits( index: String, typ: String, id: String, score: Double) 我的读者如下: object SearchLikeThisHits { import play.api.libs.functional.syntax._ implicit val searchLikeThisResult: Reads[SearchLikeThisResult] = ( (JsPath \ "total").read[Int] and (JsPath \ "max_score").read[Double] and (JsPath \ "hits").read[Seq[Hits]] )(SearchLikeThisResult.apply _) implicit val hitsReads: Reads[Hits] = ( (JsPath \ "_index").read[String] and (JsPath \ "
  • 使用 Jackson 进行 JSON 反序列化:找不到适合类型的构造函数 - 不可能提供默认构造函数或注释构造函数 [重复](JSON deserialisation using Jackson: No suitable constructor found for type - providing default constructor or annotate constructor is imposible [duplicate])
    问题 这个问题在这里已经有了答案: JsonMappingException:找不到适合类型 [简单类型,类] 的构造函数:无法从 JSON 对象实例化(14 个回答) 5年前关闭。 我使用 Jackson ObjectMapper 将对象层次结构序列化为 json String。 之后我想反序列化对象。 我有如下异常。 重要的是APINewDealArrangementImpl类层次结构超出了我的更改范围- 它是外部库的一部分。 在这种情况下,我无法实现默认构造函数,也无法使用@JsonCreator annotion 。 如何避免“找不到合适的构造函数”异常? 是否可以使用 Jackson API 中的一些自定义TypeResolverBuilder实现或其他功能来解决这个问题? 感谢帮助。 org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class com.tzero.api.transactions.TransactionState]: can not instantiate from JSON object (need to add/enable type information?) at [Source: java.io
  • Playframework 和 Twitter Streaming API(Playframework and Twitter Streaming API)
    问题 如何从 Twitter Streaming API - POST statuses/filter 读取响应数据? 我已建立连接并收到 200 状态代码,但我不知道如何阅读推文。 我只想在推文到来时打印它们。 ws.url(url) .sign(OAuthCalculator(consumerKey, requestToken)) .withMethod("POST") .stream() .map { response => if(response.headers.status == 200) println(response.body) } 编辑:我找到了这个解决方案 ws.url(url) .sign(OAuthCalculator(consumerKey, requestToken)) .withMethod("POST") .stream() .map { response => if(response.headers.status == 200){ response.body .scan("")((acc, curr) => if (acc.contains("\r\n")) curr.utf8String else acc + curr.utf8String) .filter(_.contains("\r\n")) .map(json => Try(parse
  • 从 JSON 反序列化 java 枚举(Deserialize java enum from JSON)
    问题 我们使用 Jackson 1.9.1 来序列化和反序列化 JSON 请求响应字符串到/从 Java 对象。 原始 Java 类型、集合类型和自定义对象被(反)序列化没有问题。 但是,我在尝试将 JSON 字符串反序列化为 java 枚举时遇到问题。 JSON 字符串序列化如下: "wt":{"wt":100.5,"unit":{"LBS":3}} wt 的 Java 类型如下所示: public class Weight { protected double weight; protected Unit unit; } 我在 SO 上提到了这个、这个和这个,并提出了重量单位的枚举,如下所示: public enum Unit { KG("kg"), GM("gm"), LBS("lbs"), OZ("oz"); private String value; private WeightMeasurementUnit(String value) { this.value = value; } @JsonValue public String getValue() { return this.value; } @JsonCreator public static Unit create(String val) { Unit[] units = Unit.values(); for
  • HTML模板中的playframework JsValue(playframework JsValue in HTML Template)
    问题 我正在尝试使用play(v2.2.2)的JsValue在模板中定义json对象。 问题是, "被转换为&quot; @(org: db.Tables.OrganizationRow) @import models.format.EntityFormat._ @import play.api.libs.json.Json <script type="text/javascript"> var org = @Json.toJson(org); </script> 结果是: {&quot;id&quot;:16,&quot;userid&quot;:&quot; ... more data ... }; 如何在scala html模板中获取正确的json? 回答1 将其定义为Html这样它就不会自动转义。 var org = @Html(Json.stringify(Json.toJson(org)));
  • Playframework处理后请求(Playframework handling post request)
    问题 在我的routes : POST /forms/FormValidator1/validateForm controllers.FormValidator1.validateForm(jsonForm:String) 有为该路由定义的控制器方法: def validateForm(jsonForm:String) = Action { ... 然后,我尝试通过Chrome POSTMAN插件发送POST请求(请参见上图)。 我用: 网址: http://localhost:9000/forms/FormValidator1/validateForm 标头:内容类型:application / json json数据: {名称:“我”,姓氏:“我的”} 因此,发送此POST请求时,我无法通过提到的route / url到达控制器的方法。 为什么? 更新: 有趣的是:在笔记本电脑上运行它之后(请参阅下面的答案),然后将其推入gitHub并将其拉到另一台计算机上,它开始以不同的方式工作。 现在,它抱怨说Bad Request是[Invalid XML],但是我使用了"application/json"标头,并且在提交后没有更改任何代码行。 我不知道这可能是个错误。 回答1 看来我明白了。 此处:https://groups.google.com/forum/#!topic /
  • Json.net 反序列化为 c# .net 2.0 中的对象列表(Json.net deseralize to a list of objects in c# .net 2.0)
    问题 我正在尝试将一些 json 反序列化为一个集合(列表),但我不确定哪个方法将返回一个对象列表,或者我是否必须遍历某些内容并将其复制到我自己的列表中? 谁能告诉我应该为此使用的语法或方法。 我已经用一些属性创建了我的对象,所以它可以用来保存数据了。 (标题、网址、说明) 我试过这个,但似乎不太正确 List<newsItem> test = (List<newsItem>)JsonConvert.DeserializeObject(Fulltext); 回答1 你有没有尝试查看帮助? http://james.newtonking.com/json/help/?topic=html/SerializingCollections.htm string json = @"[ { ""Name"": ""Product 1"", ""ExpiryDate"": ""\/Date(978048000000)\/"", ""Price"": 99.95, ""Sizes"": null }, { ""Name"": ""Product 2"", ""ExpiryDate"": ""\/Date(1248998400000)\/"", ""Price"": 12.50, ""Sizes"": null } ]"; List<Product> products = JsonConvert
  • GZIP PlayFramework 2.0 中的响应主体(GZIP the response body in PlayFramework 2.0)
    问题 我正在开发 Playframework 2.x 应用程序。 我的应用程序中的控制器将 JSON 响应返回给浏览器/端点。 我想知道是否有一种简单的方法可以启用响应主体的 GZIP 压缩。 回答1 gzip'ing 是非常完整的带有 Apache 前端的蛋糕。 在 Apache 2.4 上,通过使用一组基本内容类型的Location块进行 gzip 处理可能如下所示: <Location /> ... AddOutputFilterByType DEFLATE text/css application/x-javascript text/x-component text/html text/richtext image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon SetOutputFilter DEFLATE </Location> 回答2 目前在 play 2.0.4 中没有针对非资产的简单方法。 对于 Java API,您可以使用: public static Result actionWithGzippedJsonResult() throws IOException { Map<String, String> map = new HashMap<String, String>(); map.put
  • 使用 Jackson 反序列化 JSON 对象(Deseralizing JSON Objects Using Jackson)
    问题 我目前正在开发一个 Java Web 应用程序,该应用程序使用 Magento REST API 公开的 JSON 数据。 api返回的数据示例如下: {"1":{"entity_id":"1","name":"Wedding dress","description":"White wedding dress"},"2":{"entity_id":"2","name":"Sunglasses", "description":"Black sunglasses"}} 我的应用程序中有一个 Java 类,如下所示: public class Item { @JsonProperty("entity_id") private String entityId; private String name; private String description; //... } 我想对数据进行反序列化并将其转换为ArrayList<Item>但我不断收到以下错误: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token at [Source: java.io.StringReader