使用Jbuilder和Rails 3提高渲染性能

10
我正在开发的应用程序对大多数请求都以JSON对象或其集合作为响应。我们使用Jbuilder构建这些响应。渲染的数据量相当大(几千个嵌套结构中的对象 - 一旦格式化并完全展开,典型响应的JSON行数多达10,000行)。这种渲染需要很长时间 - 根据NewRelic的统计,约占总请求时间的三分之一。
我正在寻找一些指南、技巧或其他资源,以确保我从JBuilder中获得最佳性能。我也很想知道是否有可用于Jbuilder与RABL或其他类似工具的性能比较。
编辑:我找到了一个GitHub问题,抱怨Jbuilder的性能,但唯一的实际建议是“不要使用Jbuilder”。好吧,实际上,他们用了稍微强烈的措辞,但仍然没有关于为什么Jbuilder如此缓慢,是否可以采取任何措施来规避它,以及其他用于同一任务的工具如何比较的消息。
3个回答

12

jbuilder会构建一个包含您数据的大哈希值,然后使用ActiveSupport::JSON将其转换为json格式。如以下微基准测试所示,有更快速的json发射器(请确保已安装multijson和yajl-ruby宝石)。

require 'benchmark'
require 'active_support'
require 'multi_json'
sample = {menu: {
    header: "SVG Viewer",
    items: [
        {id: "Open"},
        {id: "OpenNew", label: "Open New"},
        nil,
        {id: "ZoomIn", label: "Zoom In"},
        {id: "ZoomOut", label: "Zoom Out"},
        {id: "OriginalView", label: "Original View"},
        nil,
        {id: "Quality"},
        {id: "Pause"},
        {id: "Mute"},
        nil,
        {id: "Find", label: "Find..."},
        {id: "FindAgain", label: "Find Again"},
        {id: "Copy"},
        {id: "CopyAgain", label: "Copy Again"},
        {id: "CopySVG", label: "Copy SVG"},
        {id: "ViewSVG", label: "View SVG"},
        {id: "ViewSource", label: "View Source"},
        {id: "SaveAs", label: "Save As"},
        nil,
        {id: "Help"},
        {id: "About", label: "About Adobe CVG Viewer..."}
    ]
}}


MultiJson.engine = :yajl
Benchmark.bmbm(5) do |x|
  x.report 'activesupport' do
    1000.times {ActiveSupport::JSON.encode(sample)}
  end
  x.report 'yajl' do
    1000.times {MultiJson.encode(sample)}
  end
end

在我的电脑上,这会产生以下结果

                    user     system      total        real
activesupport   1.050000   0.010000   1.060000 (  1.068426)
yajl            0.020000   0.000000   0.020000 (  0.021169)

将示例对象编码1000次,Active Support耗时略超过1秒,而MultiJson(使用yajl引擎)只需21毫秒。

JBuilder硬编码使用ActiveSupport :: JSON,但MultiJSON(一个可以在不同json库之间切换的gem)是一个微不足道的插件,并且已经是ActiveSupport的依赖项 - 请参见我的jbuilder的fork。我已经发起了一个拉取请求,但在此之前,您可以尝试使用这个分支(或创建自己的分支-它只需要一行代码更改)。


转换到MultiJson似乎可以减少大约60毫秒左右的渲染时间,对于一个典型请求而言。虽然不是我所期望的巨大变化,但对于只有一行代码的改变来说,已经很不错了。谢谢。 - MrTheWalrus
@Frederick:我看到了来自链接 https://github.com/fcheung/jbuilder/commit/a58b355f68bc39b1fddf8b178f3844c5d4f65501 的一行更改已经合并到了 Rails 主干,所以我猜我们不需要做任何更改。你能否确认我的推断是否正确? - boddhisattva
是的 - 这个拉取请求已经合并了 - 不确定它最终在哪个版本中。 - Frederick Cheung
根据Jbuilder Railscast(http://railscasts.com/episodes/320-jbuilder)所述,@FrederickCheung说它被添加为一个独立的gem,以便可以在多个Rails版本中使用。 - boddhisattva
我刚刚转换到yajl-ruby,当渲染一个包含800条记录的jbuilder视图时,我获得了巨大的性能提升。 - dc10

4
考虑切换到Rabl并添加一些caching。鉴于您有成千上万个嵌套结构的对象,您的JSON结果中的某些节点可以呈现为部分,并进行缓存 - 性能提升可能非常大。
除此之外,Rabl的性能略优于JBuilder的性能,但我发现Rabl语法有时令人困惑,一旦JBuilder实现了片段缓存,我会切换到它。

0

如前所述,JBuilder 构建哈希表,然后将该哈希表序列化为 JSON。

缓存也是如此,有主要哈希表和缓存哈希表合并到主要哈希表中,仍需要将其转换为 JSON。

我的解决方案是 TurboStreamer。TurboStreamer 直接输出到 IO/Stream/String,因此跳过了 JBuilder(乍一看这仍适用于 Rabl 和 to_json,具体取决于使用情况)的序列化步骤。

对我们来说,这显著减少了渲染时间和 GC 时间(由于在 jbuilder 中构建哈希表),并允许我们在获取结果时开始向客户端流式传输 JSON。缺点是 TurboStreamer 有点冗长和明确。

性能测试 A(不涉及缓存):

性能测试B(主要是所有缓存):


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接