【问题标题】:Slower while generating the XML from the bunch of model object从一堆模型对象生成 XML 时速度较慢
【发布时间】:2017-12-02 18:23:12
【问题描述】:
class GenericFormatter < Formatter
 attr_accessor :tag_name,:objects

 def generate_xml
   builder = Nokogiri::XML::Builder.new do |xml|
   xml.send(tag_name.pluralize) {
   objects.each do |obj|
        xml.send(tag_name.singularize){

            self.generate_obj_row obj,xml
        }                
    end
    }
   end
   builder.to_xml
 end


def initialize tag_name,objects
  self.tag_name = tag_name
  self.objects = objects
end


def generate_obj_row obj,xml
   obj.attributes.except("updated_at").map do |key,value|
     xml.send(key, value)
   end
   xml.updated_at obj.updated_at.try(:strftime,"%m/%d/%Y %H:%M:%S") if obj.attributes.key?('updated_at')
end
 end 

在上面的代码中,我已经实现了格式化程序,我使用 nokogiri XML Builder 通过操作在代码中传递的对象来生成 XML。如果数据不是太大,它会生成更快的 XML如果大于 10,000 条记录,则生成 XML 的速度会变慢,至少需要 50-60 秒。

问题: 有什么方法可以更快地生成 XML,我也尝试了 XML Builders on view 但没有用。我怎样才能更快地生成 XML?解决方案是否应该是 rails 3 上的应用程序以及优化上述代码的建议?

【问题讨论】:

  • 存储大量objects 列表是否会耗尽所有内存?您可以重构代码以处理对象in batches
  • 此外,如果您将其作为异步任务运行,那么性能将成为较少的问题。
  • 不,这不是存储庞大的列表,而是将对象操作到 xml。如果我们将其分成批次,则不会影响性能
  • objects rails 型号吗?如果是,它们有关联吗?协会有协会吗?如果是这样,您希望您的 XML 有多深?是否从数据库中获取了所有关联?有没有潜在的无限递归?
  • Aetherus Zhou 是的,它们是 Rails 模型,但只有一个模型数据没有关联数据。但是数据量很大

标签: ruby-on-rails ruby xml ruby-on-rails-3 nokogiri


【解决方案1】:

Nokogiri gem 有一个很好的界面,可以从头开始创建 XML, Nokogiri 是 libxml2 的包装器。

Gemfile gem 'nokogiri' 要生成 xml,请像这样使用 Nokogiri XML Builder

xml = Nokogiri::XML::Builder.new { |xml| 
    xml.body do
        xml.test1 "some string"
        xml.test2 890
        xml.test3 do
            xml.test3_1 "some string"
        end
        xml.test4 "with attributes", :attribute => "some attribute"
        xml.closing
    end
}.to_xml

输出

<?xml version="1.0"?>
<body>
  <test1>some string</test1>
  <test2>890</test2>
  <test3>
    <test3_1>some string</test3_1>
  </test3>
  <test4 attribute="some attribute">with attributes</test4>
  <closing/>
</body>

演示:http://www.jakobbeyer.de/xml-with-nokogiri

【讨论】:

  • 嗨 Mayur,感谢 Nokogiri 的演示。如果您在我的代码上方看到它也在 Nokogiri Builder 中,那么您对我的期望有什么新的东西。
【解决方案2】:

您的主要问题是一次性处理所有内容,而不是将数据拆分成批次。这一切都需要大量内存,首先构建所有这些 ActiveRecord 模型,然后构建整个 xml 文档的内存表示。元编程也相当昂贵(我的意思是那些send 方法)。

看看这段代码:

class XmlGenerator
  attr_accessor :tag_name, :ar_relation

  def initialize(tag_name, ar_relation)
    @ar_relation = ar_relation
    @tag_name = tag_name
  end

  def generate_xml
    singular_tag_name = tag_name.singularize
    plural_tag_name = tag_name.pluralize

    xml = ""
    xml << "<#{plural_tag_name}>"

    ar_relation.find_in_batches(batch_size: 1000) do |batch|
      batch.each do |obj|
        xml << "<#{singular_tag_name}>"

        obj.attributes.except("updated_at").each do |key, value|
          xml << "<#{key}>#{value}</#{key}>"
        end

        if obj.attributes.key?("updated_at")
          xml << "<updated_at>#{obj.updated_at.strftime('%m/%d/%Y %H:%M:%S')}</updated_at>"
        end

        xml << "</#{singular_tag_name}>"
      end
    end

    xml << "</#{tag_name.pluralize}>"
    xml
  end
end

# example usage
XmlGenerator.new("user", User.where("age < 21")).generate_xml

主要改进有:

  • 从数据库中批量获取数据,需要传递 ActiveRecord 集合而不是 ActiveRecord 模型数组
  • 通过构造字符串生成xml,这样有产生无效xml的风险,但是比使用builder快很多

我在超过 60k 条记录上对其进行了测试。生成这样的 xml 文档大约需要 40 秒。

要进一步改进这一点,还有很多工作可以做,但这完全取决于您的应用程序。

这里有一些想法:

  • 不要使用 ActiveRecord 来获取数据,而是使用更轻量级的库或普通的数据库驱动程序
  • 只获取您需要的数据
  • 调整批量大小
  • 将生成的 xml 直接写入文件(如果这是您的用例)以节省内存

【讨论】:

  • 嘿,Michal,所以基本上你正在将字符串生成为 XML,所以我如何能够以纯 XML 格式获取它,因为我想如果我将调用该方法 to_xml 所以它也需要更多时间在字符串上生成字符串。
  • 我不知道你打算如何使用这个类。如果某些代码正在调用to_xml,那么只需将generate_xml 的名称更改为to_xml
  • 感谢 michal Little 应用此功能可提高性能,因此可以轻松集成。对你给它的分数很好。
猜你喜欢
  • 2014-03-07
  • 2010-09-06
  • 1970-01-01
  • 2021-11-03
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多