从流块写入文件

时间:2015-08-30 16:16:48

标签: ruby asynchronous sinatra thin puma

处理有时需要返回大文件的Web服务,并希望它能够快速向客户端发送内容,以便客户端不会等待数据的启动。 stream似乎是完美的,但我遇到了一个问题。

哑巴的例子:

get '/path' do
  status 200
  headers 'Content-Type' => 'text/plain'
  stream do |out|
    sleep 1
    out << "Hello,\n"
    sleep 1
    out << "World!\n"
  end
end

这很好用:

$  curl http://localhost:4567/path
   Hello,
   World!

但是我有一个服务写入的副本日志,并尝试将文件I / O与流API混合起来根本不起作用:

get '/path' do
  status 200
  headers 'Content-Type' => 'text/plain'
  File.open '/tmp/side-log', 'a' do |lf|
    stream do |out|
      lf.puts "Woo!"
      sleep 1
      out << "Hello,\n"
      sleep 1
      out << "World!\n"
    end
  end
end

现在我明白了:

$ curl http://localhost:4567/path
curl: (18) transfer closed with outstanding read data remaining

Puma没有表明服务器端存在任何问题,但Thin完全退出:

hello2.rb:13:in `write': closed stream (IOError)
        from hello2.rb:13:in `puts'
        from hello2.rb:13:in `block (3 levels) in <main>'
        from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:437:in `block (2 levels) in stream'
        from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:628:in `with_params'
        from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:437:in `block in stream'
        from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:403:in `call'
        from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:403:in `block in each'
        from vendor/bundle/gems/eventmachine-1.0.8/lib/eventmachine.rb:1062:in `call'
        from vendor/bundle/gems/eventmachine-1.0.8/lib/eventmachine.rb:1062:in `block in spawn_threadpool'
[1]+  Exit 1                  ruby hello2.rb

那么,如果我想在流块内输出除输出流之外的其他地方,我该怎么办?

1 个答案:

答案 0 :(得分:2)

不确定这是否是最佳解决方案,但使用异步em-files gem对我有用,即使在Puma中(我理解不是基于EventMachine):

require 'em-files'  

get '/path' do
  status 200
  headers 'Content-Type' => 'text/plain'
  EM::File.open '/tmp/side-log', 'a' do |lf|
    stream do |out|
      lf.write "Woo!\n"
      sleep 1
      out << "Hello,\n"
      sleep 1
      out << "World!\n"
   end
  end
end