4

我有一个 Sinatra 网络应用程序,我非常想通过流式更新来增强某些功能。不过,现在,我只是想了解如何使用流数据,这是我以前从未做过的。我有以下简单的测试代码:

在西纳特拉:

get '/foo' do
  stream do |out|
    10.times do
      out.puts "foo"
      out.flush
      sleep 1
    end
  end
end

get '/bar' do
  erb :bar
end

bar.erb

<body>
  <div class="stream">
    nothing.
  </div>
</body>

<script type="text/javascript" charset="utf-8">
  $(document).ready( function() {
    $.get('/foo', function(html) {
      $(".stream").html(html);
    });
  });
</script>

我并不感到惊讶,这并没有达到我想要的效果,即在编写每个 'foo' 并动态更新页面时获取它。相反,大约 10 秒内什么都没有发生,然后我得到foo foo foo foo foo foo foo foo foo foo foo.

我的问题是,如何在 ERB 模板(使用 Ruby、jQuery 或其他方式)中提取提供的流数据,而不是阻塞直到它全部收集并一次全部吐出?

4

2 回答 2

5

Sinatra 动作包装了整个 HTTP 响应周期——这意味着它会等到动作完成后再关闭请求,此时浏览器认为数据“完整”且“良好”可以使用。您在上面的代码中创建的只是一个非常非常慢的 Sinatra 操作。

您正在寻找的技术是 Websockets,大多数现代浏览器都支持它,并在每个客户端和服务器之间提供双向通信通道。websocket 通道是通过“升级”常规 HTTP 请求来创建的。在客户端不支持 Web 套接字的情况下,可以使用 HTTP 长轮询等技术来模拟它们(其中请求保持打开状态,没有响应,直到有可用数据 - 那时数据被分流到响应通道,响应通道已关闭,预计客户端将打开一个新请求以获取任何进一步的数据)。

您可以使用 EventMachine 和EM-Websocket在您的 Ruby 应用程序中进行此设置。另一种方法是Socky,我相信它提供了 javascript 客户端和 Ruby 服务器。

于 2012-06-05T19:06:28.680 回答
0

如果您在 Sinatra 中使用流,则会跳过普通模板,您会得到一个只有您正在流式传输的 html 的空白页面。您可以通过手动创建布局并将其与文本一起流式传输来规避此问题。这里举个例子。

require 'sinatra'
require "sinatra/streaming"
set server: 'thin', connections: []

get '/' do
  stream do |out|
    settings.connections << out
    @out = out
    erb :stream
    out.callback { settings.connections.delete(out) }
  end
end
__END__
@@pre
  <!DOCTYPE html>
  <html>
  <head>
    <title>test</title>
  </head>
  <body>
   <h1>This is the body</h1>

@@after
  </body>
  </html>

@@stream
  <%
    @out.puts erb(:pre)
    @out.puts "<h2>test</h2>"
    (1..10).each do |i|
        @out.puts "#{i}<br>"
        sleep 2
      end
    @out.puts erb(:after)
    @out.flush
  %>
于 2017-02-08T14:26:25.497 回答