2012-06-05 24 views
4

我有一個Sinatra網絡應用程序,我非常想增強流媒體更新某些功能。不過,現在我只是想學習使用流式數據的方法,這是我以前從未做過的。我有以下簡單的測試代碼:流與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或其他方式)中提取流數據,而不是一直阻塞,直到全部收集並一次性將它全部吐出?

回答

5

Sinatra動作包裝整個HTTP響應週期 - 這意味着它會等到動作完成才關閉請求,此時瀏覽器會認爲數據「完整」和「良好」以供使用。您在上面的代碼中創建的所有內容都是非常非常慢的Sinatra操作。

您正在尋找的技術是Websockets,它受到大多數現代瀏覽器的支持,並提供每個客戶端與服務器之間的雙向通信通道。 websocket通道是通過「升級」常規HTTP請求創建的。在客戶端不支持Web套接字的情況下,可以使用諸如HTTP Long Polling(HTTP長輪詢)之類的技術來模擬這些套接字(其中,在沒有響應的情況下請求保持打開狀態,直到有數據可用 - 此時數據被分流到響應信道,響應頻道關閉,客戶需要打開一個新的請求以獲取更多數據)。

您可以使用EventMachine和EM-Websocket在Ruby應用程序中獲得此設置。另一種選擇是Socky,我相信它提供了JavaScript客戶端以及Ruby服務器。

+0

謝謝指點。我很失望Sinatra'stream'塊不能按照我設想的方式工作,但我認爲這會有點太好,不可能是真的。 EM-Websocket看起來很不錯,我會開始閱讀。 – asfallows

+2

請注意,Erb或Haml不以任何流式傳輸方式提供內容,因此Sinatra無法傳輸任何響應。但是,Sinatra *可以*自己發送流式響應,* iff *您有流式傳輸內容供其使用。 – Phrogz

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 
    %>