Rails 流媒体:"render stream: true" 不工作

Rails streaming: "render stream: true" not working

我最近发现了 rails 流式传输,并且在阅读了各种文档后,我尝试将其添加到我的一个控制器中,如下所示:

def index
    render :stream => true
end

我的模板文件目前看起来像这样:

<%10.times do%>
  <p>
    I should get streamed ERB...
    <%sleep 0.5%>
  </p>
<%end%>

但是它不是每 0.5 秒显示一条消息,而是等待 5 秒并显示整个页面!我已经在浏览器中使用 curl 检查了这种行为。

我在 OSX 上使用独角兽,是的,我配置了 unicorn.rb 用于流式传输:

listen 3000, :tcp_nodelay => true, :tcp_nopush => false
worker_processes 1

如果你想看我的布局文件,它看起来像这样:

<!DOCTYPE html>
<html>
<head>
  <title>StreamingTest</title>
  <%= stylesheet_link_tag    "application" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body>

<%= yield %>

</body>
</html>

我尝试禁用所有 gem(rails 和 postgres 除外),并将我的控制器、布局和模板削减到最低限度,但没有任何成功。

我什至下载了我找到的完整流式传输演示 (https://github.com/slim-template/slim-streamingtest),当我 运行 下载时,它也没有流式传输! (请注意任何尝试 运行 该演示的人:在 "bundle install" 之前,我必须将 gemfile 源更改为使用 https 而不是 http,并且我必须 "bundle update sprockets" 才能 运行)

我注意到其他人(使用 Puma)似乎有类似的问题,但尚未成功解决:

Rails Streaming not Streaming

可能对我有用的东西对他们也有用。

Streaming 确实可以帮助我们的应用程序,如果我能让它正常工作的话,但我什至无法 demo 将应用程序流式传输到 运行 正确!我怀疑这可能与我的开发环境有关,但是当我将我的应用程序部署到 Heroku 时问题仍然存在,而且我没有想法。任何帮助将不胜感激 。 . .

谢谢!

我试过你提到的演示回购,行为符合预期。在您展示的示例中,它将停止流式传输模板,然后等待它在 5 秒后完全呈现并发送其余部分。如果你使用 curl:

# curl -i http://localhost:3000

HTTP/1.1 200 OK
Date: Thu, 21 Apr 2016 17:29:00 GMT
Connection: close
Cache-Control: no-cache
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
X-UA-Compatible: IE=Edge
X-Runtime: 0.018132

<!DOCTYPE html>
<html>
  <head>
    <title>StreamingTest</title>
    <link href="/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
    <script src="/javascripts/application.js" type="text/javascript"></script>
    <meta content="authenticity_token" name="csrf-param" />

然后等待模板渲染完成5秒后发送。

  ...
  <p>
    I should get streamed...
  </p>
</body></html>

这是应该发生的事情。它不会每 0.5 秒发送一次部分内容。

使用模板进行流式传输的主要目标是让浏览器在接收其余内容之前接收 headers,这样可以节省加载资产的时间。如所述 in the docs.

Streaming inverts the rendering flow by rendering the layout first and streaming each part of the layout as they are processed. This allows the header of the HTML (which is usually in the layout) to be streamed back to client very quickly, allowing JavaScripts and stylesheets to be loaded earlier than usual.

它不会发送部分,除非它已经完全呈现。

但是,为了向您展示如何获得您期望的那种行为,您可以像这样更改 application.html.slim 布局的 body:

  body
    = yield
    = render 'application/other'

并创建一个名为 _other.html.erb 的部分,内容如下

<% sleep 1 %>
<p>Me too!</p>

现在您将在 curl 中看到它渲染布局的开始,等待渲染在 index.html.erb 上完成,发送它并显示在 curl 中,然后等待 _other.html.erb 部分完成渲染以结束请求 body。瞧。

您也可以直接提供请求 body,每隔几毫秒发送一次内容,您可以执行以下操作:

def index
  headers['Cache-Control'] = 'no-cache'
  self.response_body = Enumerator.new do |yielder|
    10.times do
      yielder << "I should be streamed...\n"
      sleep 0.3
    end
  end
end

然而,这并不意味着发送 html,尽管您可以使用 render_to_string,但您必须在代码中进行大量修改。

请注意,所有这些都是 Rails 3.x,对于 4.x,您必须使用 ActionController::Live。在ActionController::Live的情况下,有关于如何在js中监听服务器发送的事件以呈现客户端的示例,例如mini-chat