【问题标题】:Respond with large amount of objects through a Rails API通过 Rails API 响应大量对象
【发布时间】:2015-12-28 12:46:13
【问题描述】:

我目前有一个用于我的一个项目的 API 和一项服务,该服务负责将导出文件生成为 CSV、存档并将它们存储在云中的某个位置。

由于我的 API 是用 Rails 编写的,而我的服务是用纯 Ruby 编写的,所以我在服务中使用 Her gem 与 A​​PI 进行交互。但是我发现我当前的实现性能较差,因为我在我的服务中做了一个Model.all,这反过来又会触发一个请求,该请求可能在响应中包含太多对象。

我很好奇如何改进整个任务。这是我的想法:

  • 在 API 级别实现分页并从我的服务中调用 Model.where(page: xxx)
  • 在 API 级别生成实际的 CSV 并将 CSV 发送回服务(这可以同步或异步完成)。

如果我使用第一种方法,我应该在每页检索多少个对象?响应应该有多大?

如果我使用第二种方法,这会给请求带来相当大的开销(我猜 API 请求应该不会花那么长时间),我还想知道这是否真的是 API 的工作。

我应该遵循什么方法?或者,我还缺少什么更好的东西?

【问题讨论】:

    标签: ruby-on-rails api rest csv export


    【解决方案1】:

    您需要通过 ruby​​ 进程传递大量信息,这始终不简单,我认为您在这里没有遗漏任何内容。

    如果您决定在 API 级别生成 CSV,那么维护服务可以获得什么?您可以完全放弃该服务,因为用 nginx 代理替换您的服务会做得更好(如果您只是从 API 主机流式传输响应)?

    如果您决定分页,肯定会降低性能,但没有人能准确告诉您应该分页多少 - 更大的页面会更快并消耗更多内存(通过运行更少的工作人员来降低吞吐量) ,较小的页面会更慢,消耗更少的内存,但由于 IO 等待时间需要更多的工作人员,

    确切的数字将取决于您的 API 应用程序和云以及您的基础架构的 IO 响应时间,恐怕没有人可以给您一个简单的答案,您可以在不进行压力测试实验的情况下遵循,并且一旦您设置压力测试,无论如何你都会得到一些你自己的——比任何人的估计都要好。

    一个建议,写更多关于你的问题,你正在工作的限制等,也许有人可以帮助你更激进的解决方案。出于某种原因,我觉得您真正需要的是像 sidekiq 或延迟作业这样的后台处理器,或者如果您急于解耦您的应用程序或 nginx,则可以通过数据库视图将您的服务直接连接到数据库API 响应的代理,或者什么都没有……但如果没有更多信息,我真的无法判断。

    【讨论】:

    • 感谢您分享您的意见!为了隔离,我不想直接从服务连接到数据库;然后我需要在多个地方使用模型,并且由于该服务不是 Rails,所以我很难做到这一点(已经经历过这个)。我也不能放弃服务;此 CSV 功能只是服务功能的一部分。关于后台处理,你参考我的第二点,做异步方式?
    • 关于约束,我只是想解耦组件; CSV 生成每天只会发生一次,并且由于它在一个单独的服务中,我想如果它需要更多的运行时间,它不会影响任何东西。
    • 如何在 cron 作业或后台处理器的 API 应用程序中生成 CSV 并在请求时由服务提供它?
    • 另一种选择是创建一个 DB 视图,从 API 项目维护它并从服务访问它 - 将视图视为等同于 API 为服务提供服务的控制器操作 - 如果 DB 发生变化您也可以更改视图以返回 CSV 功能所需的内容,您可以简单地通过原始 ruby​​ 从中选择或在项目中包含“just”ActiveRecord,通常在这些行中 - rietta.com/blog/2013/11/28/rails-and-sql-views-for-a-report
    • 感谢您的回答和详细说明。将其标记为已接受并奖励赏金。
    【解决方案2】:

    我认为这真的取决于您想要如何定义“性能”以及您的 API 目标是什么。您是否想确保对您的 API 的请求的响应时间不超过 20 毫秒,而不是添加分页是一种合理的方法。特别是如果 CSV 生成只是一个边缘案例,并且 API 确实是为其他服务构建的。然后,每页的项目数量将受到您交付它们的速度的限制。您的服务不会特别高效(甚至更低),因为它需要多次调用该服务。

    如果您认为服务转储整个记录集的有效用例,则值得将创建异步调用(可能使用 webhook 作为回调)添加到您的 API。

    话虽如此,我认为严格来说,快速响应是 API 的工作。因此,也许尝试弄清楚缓存如何提高响应时间,因此对所有记录进行分页是合理的。另一方面,服务的工作是注意对 API 的调用量,因此可能会在本地存储旧记录并仅轮询更新,而不是每次都转储整个记录集。

    【讨论】:

    • 感谢您的帖子。事实上,CSV 生成是一个边缘案例,通常服务不需要从 API 获取那么多数据。关于限制,由于我从 API 发送回序列化对象,它们会有一些字节大小,我想知道响应可能有多大(大小)。关于 async 和 webhook,由于这是一种罕见的操作,我想我会让我的服务应用程序“等待”,直到 API 发送所有内容;或者在这种情况下异步真的值得吗?作为结论,您是否有其他方法?因为我觉得你说这两个选项都不够好。
    • 好吧,我在那种情况下,我很可能也会采用务实的方法,并在 api 端创建 CSV,也许还有另一个 respond_to 块。除非我们谈论几(几十)兆字节,否则大小应该不是问题。那么内存消耗可能是一个问题。 Ruby Socket 连接有默认的响应超时,我相信这可能会在大小限制之前达到。
    • 流式传输响应可能是降低对内存和数据库影响的好方法。 This 可能会有所帮助。
    • 分页需要大量 IO 时间,也使异常处理复杂化,将响应推送到 find_each 加载具有 1000 条记录的大页面的失败经验,在小型数据集上这是不必要的,在大型数据集上根本不可行,无论 Rails 驱动的解决方案可以提供什么至少比往返 postgres 慢一个数量级
    猜你喜欢
    • 2017-05-25
    • 1970-01-01
    • 1970-01-01
    • 2020-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多