与Sinatra一起流式传输,逐步更新目的地

Stream with Sinatra, update destination gradually

本文关键字:更新 目的地 传输 Sinatra 一起      更新时间:2023-09-26

我有一个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 或其他方式)中拉取提供的流数据,而不是阻塞直到全部收集并一次将其全部吐出?

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

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

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

如果您在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
  %>