跳至内容 跳至搜索

Action Controller 流式传输

允许在渲染时将视图流式传输回客户端。

默认情况下,Rails 通过首先渲染模板,然后渲染布局来渲染视图。整个模板渲染完毕、所有查询完成、布局处理完后,响应才会发送到客户端。

流式传输颠倒了渲染流程,首先渲染布局,然后在处理时依次渲染布局的每个部分。这样可以将 HTML 的头部(通常在布局中)非常快地流式传输回客户端,使 JavaScript 和样式表比平时更早加载。

一些 Rack 中间件可能无法正常工作,在流式传输时需要小心。这将在下面更详细地介绍,请参阅 Streaming 中的中间件 部分。

可以轻松地将流式传输添加到给定模板,只需将 :stream 选项传递给 render 即可。

class PostsController
  def index
    @posts = Post.all
    render stream: true
  end
end

何时使用流式传输

对于像 newedit 这样的轻量级操作,流式传输可能被认为是过度的。流式传输的真正好处在于那些需要大量数据库查询的昂贵操作。

在这样的操作中,您希望尽可能地延迟查询执行。例如,想象一下以下 dashboard 操作

def dashboard
  @posts = Post.all
  @pages = Page.all
  @articles = Article.all
end

这里的大多数查询都在控制器中进行。为了从流式传输中获益,您需要将其重写为

def dashboard
  # Allow lazy execution of the queries
  @posts = Post.all
  @pages = Page.all
  @articles = Article.all
  render stream: true
end

请注意,:stream 仅适用于模板。使用 :stream 渲染 :json:xml 将不起作用。

布局和模板之间的通信

在流式传输时,渲染是从上到下进行的,而不是从内到外。Rails 从布局开始,模板在到达其 yield 时被渲染。

这意味着,如果您的应用程序当前依赖于在模板中设置的实例变量在布局中使用,那么一旦您切换到流式传输,它们将不再起作用。无论您是否使用流式传输,在布局和模板之间进行通信的正确方法是使用 content_forprovideyield

以一个简单的例子来说明,布局期望模板告诉它使用哪个标题。

<html>
  <head><title><%= yield :title %></title></head>
  <body><%= yield %></body>
</html>

您可以在模板中使用 `content_for` 来指定标题。

<%= content_for :title, "Main" %>
Hello

最终结果将是

<html>
  <head><title>Main</title></head>
  <body>Hello</body>
</html>

但是,如果 `content_for` 被多次调用,最终结果将包含所有调用的连接。例如,如果我们有以下模板

<%= content_for :title, "Main" %>
Hello
<%= content_for :title, " page" %>

最终结果将是

<html>
  <head><title>Main page</title></head>
  <body>Hello</body>
</html>

这意味着,如果您在布局中使用 `yield :title` 并且想要使用流式传输,您将不得不渲染整个模板(并最终触发所有查询),然后才能流式传输标题和所有资产,这违背了流式传输的目的。或者,您可以使用一个名为 `provide` 的助手,它与 `content_for` 的作用相同,但它告诉布局停止搜索其他条目并继续渲染。

例如,上面使用 `provide` 的模板将是

<%= provide :title, "Main" %>
Hello
<%= content_for :title, " page" %>

结果是

<html>
  <head><title>Main</title></head>
  <body>Hello</body>
</html>

也就是说,在流式传输时,您需要正确检查您的模板,并选择何时使用 `provide` 和 `content_for`。

有关更多信息,请参见 ActionView::Helpers::CaptureHelper

标题、Cookie、会话和闪存

在流式传输时,HTTP 标头会在渲染第一行之前发送到客户端。这意味着,在模板开始渲染后修改标头、Cookie、会话或闪存不会传播到客户端。

中间件

需要操作主体内容的中间件无法与流式传输一起使用。您应该在开发或生产环境中流式传输时禁用这些中间件。例如,`Rack::Bug` 在流式传输时将无法使用,因为它需要在 HTML 主体中注入内容。

`Rack::Cache` 也无法与流式传输一起使用,因为它尚不支持流式传输主体。在流式传输时,`Cache-Control` 会自动设置为“no-cache”。

错误

在流式传输时,异常会变得更加复杂。这是因为模板的一部分已经渲染并流式传输到客户端,这使得无法渲染整个异常页面。

目前,当在开发或生产环境中发生异常时,Rails 会自动流式传输到客户端

"><script>window.location = "/500.html"</script></html>

前两个字符(`">"`)是必需的,以防异常发生在为给定标签渲染属性时。您可以在日志中检查异常的真实原因。

Web 服务器支持

并非所有 Web 服务器都开箱即用地支持流式传输。您需要检查每个服务器的说明。

Unicorn

Unicorn 支持流式传输,但需要配置。为此,您需要创建一个如下所示的配置文件

# unicorn.config.rb
listen 3000, tcp_nopush: false

并在初始化时使用它

unicorn_rails --config-file unicorn.config.rb

您可能还想配置其他参数,例如 :tcp_nodelay

有关更多信息,请查看 文档

如果您将 Unicorn 与 NGINX 一起使用,则可能需要调整 NGINX。流式传输应该在 Rainbows 上开箱即用。

乘客

Phusion Passenger 与 NGINX 一起使用,开箱即用地提供两种流式传输机制。

  1. NGINX 响应缓冲机制,它取决于 passenger_buffer_response 选项的值(默认值为“关闭”。)。

  2. Passenger 缓冲系统,始终处于“开启”状态,无论 passenger_buffer_response 的值如何。

passenger_buffer_response 设置为“开启”时,流式传输将在 NGINX 级别完成,它将等待应用程序完成将响应发送回客户端。

有关更多信息,请查看 文档