【问题标题】:Read large PUT or POST request body as a stream in Rails without buffering to disk在 Rails 中将大型 PUT 或 POST 请求正文作为流读取,而无需缓冲到磁盘
【发布时间】:2020-11-11 22:48:53
【问题描述】:

我正在为 API 用例试验 Rails 6。用户会发布大量请求,这些请求会被 Rails 接收并流式传输到另一个地方。这可能会使用 JRuby 运行,但现在我正在使用 MRI Ruby 进行测试。网络服务器是 Puma(开箱即用的导轨安装)。

有没有办法在上传请求主体时将其读取为流,或者 Rails 是否需要等待整个请求上传才能访问它?

使用这个 Curl 命令:

curl  -i -X PUT -T data.bin "http://localhost:3000/welcome/stream"

还有这个简单的控制器:

  def stream
    puts "The stream was called"

    io = request.body
    until io.eof?
      io.read(1024*1024)
      puts "Read 1MB"
    end

    head 200, content_type: "text/html"
  end

似乎整个请求在 rails 访问它之前就暂存在某个地方,因为在我的任何 put 出现在控制台之前有很长的延迟。

搜索了一下,似乎是缓存数据的网络服务器(Puma、Unicorn 等),它只有在收到所有数据后才调用 Rails。

有没有办法在流进来时开始读取它或者避免缓冲它?

是 Rack 还是 Puma 进行缓冲?如果是,它在哪里缓冲数据?

【问题讨论】:

  • 介意分享回购吗?
  • 找到缓冲的位置:github.com/puma/puma/blob/master/lib/puma/server.rb#L501-L502。您示例中的io 变量的类型为Tempfile。如果请求正文大于 112KB,则首先将正文作为一个整体下载到临时文件中,然后才将其传递给 Rails 控制器。据我了解,以流式方式将文件转储到 Tempfile,不消耗任何 RAM。至于你问题的另一部分——如何避免这种缓冲——我还没有答案。
  • 嗯,一种选择是从 puma 猴子修补 MAX_BODY 常量,但它可能会严重破坏事情。
  • 在我将 MAX_BODY 更改为更大的常量后,没有任何问题,但它仍在缓冲主体,这次是在内存中。
  • 可能相关(虽然帖子很旧):stackoverflow.com/questions/4795205/….

标签: ruby-on-rails ruby rack


【解决方案1】:

是 Rack 还是 Puma 进行缓冲?如果是,它在哪里缓冲数据?

服务器在将整个流传递给 Rack/Rails 之前收集整个流...因此您将无法在主体完全上传之前开始处理请求。

这种设计有充分的理由,因为服务器将使用可用线程来轮询 IO 而不是阻塞,从而允许您的应用程序在后台执行 IO 操作的同时处理其他请求。

否则,当您的应用程序在不完整的流上调用read 时,IO 会阻塞线程(进而阻塞服务器),从而导致并发性不稳定。

有没有办法在流进来时开始读取它或者避免缓冲它?

您可以实现自己的服务器(不推荐),也可以使用 AJAX / WebSockets 上传点点滴滴(更常见的方法)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-11
    • 2010-11-06
    • 1970-01-01
    相关资源
    最近更新 更多