瘦asynch应用程序示例如何缓冲对身体的响应?

时间:2012-06-28 13:35:05

标签: ruby rack eventmachine thin

您好我已经阅读了有关Thin的文档,我对eventmachine来说相当新,但我知道Deferrables如何工作。我的目标是了解当身体被推迟并逐个部分流式传输时瘦是如何工作的。

以下是我正在努力解决的例子。


class DeferrableBody
  include EventMachine::Deferrable

  def call(body)
    body.each do |chunk|
      @body_callback.call(chunk)
    end

   # @body_callback.call()
  end

  def each &blk
    @body_callback = blk
  end

end

class AsyncApp

  # This is a template async response. N.B. Can't use string for body on 1.9
  AsyncResponse = [-1, {}, []].freeze
  puts "Aysnc testing #{AsyncResponse.inspect}"

  def call(env)

    body = DeferrableBody.new    

    # Get the headers out there asap, let the client know we're alive...
    EventMachine::next_tick do
       puts "Next tick running....."
       env['async.callback'].call [200, {'Content-Type' => 'text/plain'}, body] 
     end

    # Semi-emulate a long db request, instead of a timer, in reality we'd be 
    # waiting for the response data. Whilst this happens, other connections 
    # can be serviced.
    # This could be any callback based thing though, a deferrable waiting on 
    # IO data, a db request, an http request, an smtp send, whatever.
    EventMachine::add_timer(2) do
      puts "Timer started.."
      body.call ["Woah, async!\n"]

      EventMachine::add_timer(5) {
        # This could actually happen any time, you could spawn off to new 
        # threads, pause as a good looking lady walks by, whatever.
        # Just shows off how we can defer chunks of data in the body, you can
        # even call this many times.
        body.call ["Cheers then!"]
        puts "Succeed Called."
        body.succeed
      }
    end

    # throw :async # Still works for supporting non-async frameworks...
    puts "Async REsponse sent."
    AsyncResponse # May end up in Rack :-)
  end

end

# The additions to env for async.connection and async.callback absolutely 
# destroy the speed of the request if Lint is doing it's checks on env.
# It is also important to note that an async response will not pass through 
# any further middleware, as the async response notification has been passed 
# right up to the webserver, and the callback goes directly there too.
# Middleware could possibly catch :async, and also provide a different 
# async.connection and async.callback.

# use Rack::Lint
run AsyncApp.new

我不清楚理解的部分是在calleach方法中DeferrableBody类中发生的事情。

我得到的是,一旦计时器作为存储在@body_callback中的块触发,每个都接收到数据块,并且当它发送身体但是当yieldcall被调用时,身体上的成功被调用那些阻止它在发送时如何成为单个消息。

我觉得我不明白关闭到足以理解发生了什么。非常感谢任何帮助。

谢谢。

1 个答案:

答案 0 :(得分:2)

好的我想我弄清楚每个块是如何工作的。

post_init上的精简似乎在连接进入时生成@request@response对象。响应对象需要响应each方法。这是我们覆盖的方法。

env['async.callback']是一个闭包,分配给post_process类方法中名为connection.rb的方法,其中数据实际发送到连接,如下所示


      @response.each do |chunk|        
        trace { chunk }
        puts "-- [THIN] sending data #{chunk} ---"
        send_data chunk
      end

如何定义响应对象


 def each
      yield head

      if @body.is_a?(String)
        yield @body
      else

        @body.each { |chunk| yield chunk }
      end
    end

所以我们的env ['async.callback']基本上是一个名为post_process的方法,在通过method(:post_process)访问的connection.rb类中定义,允许我们的方法像封闭一样处理,包含访问权限到@response对象。当反应堆启动时,它首先在next_tick中发送头部数据,然后它产生头部,但此时身体是空的,因此不会产生任何结果。

在此之后,我们的each方法会覆盖@response对象拥有的旧实现,因此当add_timers触发时,触发的post_process会发送我们提供的数据body.call(["Wooah..."])到浏览器(或任何地方)

完全敬畏macournoyer和团队承诺瘦身。如果你觉得这不是它的工作方式,请纠正我的理解。