【问题标题】:Rails + Puma + Transfer-Encoding: chunked response - supported or not?Rails + Puma + Transfer-Encoding:分块响应 - 是否支持?
【发布时间】:2019-01-13 19:44:19
【问题描述】:

配置

  • 导轨:4.2.7.1
  • 彪马:3.8.2

--

Transfer-Encoding:  chunked

我一直无法完成这项工作,也无法找到明确的答案:在上述配置中,我想将大量数据流式传输到客户端(在响应中) - 是否支持?

  • 如果是这样,我的责任是什么?
    • 我的控制器是否应该发出十六进制块大小,\r\n0 等?
  • 感觉就像我想要但找不到简单的响应 API,例如:
    • write, write, write, flush
    • write, write, write, flush
    • close
  • 我已经阅读了 100 篇关于 Rack、猴子补丁和其他疯狂的帖子
  • 我读过关于 Puma 和/或 Rack 破坏编码的信息,可能 gzip/放气顺序错误
  • 这似乎是一个简单的功能,应该很容易获得,但我很难过
  • 我创建了许多测试,例如self.response_body = Enumerator.newresponse.stream.write 等 - 都具有相似的结果(通过 curl) - Malformed encoding found in chunked-encodingtransfer closed with outstanding read data remaining

谁能给我看灯?

【问题讨论】:

    标签: ruby-on-rails puma transfer-encoding


    【解决方案1】:

    ActionController::Live API 提供您所描述的内容:response.stream.writeresponse.stream.close。 (write 自动刷新一个块;如果这对您不起作用,您需要自己进行缓冲。)

    只要您include ActionController::Live(并注意这会影响整个控制器的行为,而不仅仅是一个动作),您就应该能够毫不费力地编写流式响应:您不需要也不应该,设置与分块相关的任何标题等。

    其他参考:http://tenderlovemaking.com/2012/07/30/is-it-live.html

    【讨论】:

    • 不完全是 - 您引用的 API 设置了并非所有浏览器都支持的 Content-Type: text/event-stream 标头。 (AFAIK)另外,由于线程实现,我不认为这是一个可扩展的解决方案。
    • example 使用该内容类型;你可以设置你喜欢的任何东西。
    • 我认为你很困惑 - Transfer-Encoding 而不是 Content-Type 标头与此讨论相关。想必你知道Transfer-Encoding: chunked 是如何工作的,以及客户端是如何处理它的。 Content-Type: text/event-stream 产生服务器端事件,并非所有客户端(浏览器)都支持这一点。另一方面,所有浏览器都支持Transfer-Encoding: chunked
    • def show response.headers["Transfer-Encoding"] = "chunked" 100.times { response.stream.write "hello world\n" } ensure response.stream.close end 结果:HTTP /1.1 200 OK Transfer-Encoding: chunked Cache-Control: no-cache Content-Type: text/html; charset=utf-8 连接:关闭服务器:thin curl:(56)分块编码@matthewd 中的十六进制序列非法或丢失
    • 你不需要,也不应该,设置任何与分块相关的标题等。 (另外,瘦不是美洲狮。)
    【解决方案2】:

    @matthewd 精彩答案的重要附录:

    Rack 规范支持通过响应对象上的the use of the each method 或通过the use of hijack 进行流式响应。

    @matthewd 说得对:

    ActionController::Live API 提供您所描述的内容...

    但是,该实现要么劫持套接字,要么使用 Rack 规范的“hack”和 each 方法。

    最好的情况是实现劫持套接字并在新线程上运行它(这是它通常应该做的,AFAIK)。

    但是,这可能会导致大量线程,并且可能会导致性能下降 - 堆栈数据的内存空间线程成本(每个线程/客户端 1Mb-2Mb)和上下文切换变得更加昂贵随着更多线程的创建。

    在最坏的情况下,一个缓慢的 each 循环会阻塞服务器的线程,使服务器瘫痪并最终导致 DoS 情况。

    正确的答案应该是不要通过单个 HTTP 请求流式传输数据 - 改用 本机* WebSockets、SSE 或 AJAX 解决方案。

    另一种半正确的方法是将所有数据保存到临时文件中,并使用支持 Ruby 层之外的静态文件流式传输的服务器(例如 iodine)或代理(例如 nginx)发送文件。

    * 原生:原生 WebSocket / SSE 解决方案遵循this Rack proposal 并允许服务器处理网络层而不是运行另一个线程 / IO 反应器。详情请见this blog post

    【讨论】:

      猜你喜欢
      • 2023-03-09
      • 1970-01-01
      • 2018-02-28
      • 1970-01-01
      • 2011-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多