无法让ActionController :: Live与ActiveSupport :: Notifications一起玩得很好

时间:2014-01-30 11:07:18

标签: ruby-on-rails events stream notifications actioncontroller

我一直在寻找使用ActionController :: Live触发服务器端事件的方法,而不使用轮询。似乎Live的唯一记录的pub / sub解决方案是使用Redis,但我真的很想使用我目前拥有的堆栈。我遇到了ActiveSupport :: Notifications,它似乎正是我想要的。

但是,当我几乎使用Redis时,Live会抛出IOError并关闭流。我没有看到为什么会发生这种情况的任何正当理由。

例如,这可能有效:

def stream
    response.headers['Content-Type'] = 'text/event-stream'
    redis = Redis.new
    redis.subscribe('namespaced:stream') do |on|
        response.stream.write "hello world\n"
    end
    rescue IOError
        # Client Disconnected
    ensure
        response.stream.close
end

但是当我尝试这个时,我得到一个IO错误:

def stream
    response.headers['Content-Type'] = 'text/event-stream'
    ActiveSupport::Notifications.subscribe("process_action.action_controller") do |*args|
        response.stream.write "hello world\n"
    end
    rescue IOError
        # Client Disconnected
    ensure
        response.stream.close
end

我真的不知道为什么会这样。服务器日志中没有任何内容表示存在实际问题,否则通知订阅会起作用;我可以告诉,因为即使我收到IO错误,我仍然可以获得订阅,当发生“process_action.action_controller”通知时将字符串放入控制台。

当通知没有发生时,连接工作正常但是只要有通知(我认为应该只向流写“hello world”),我就会得到那个愚蠢的IO错误。< / p>

顺便说一句,我的确计划自行通知;只需使用现有通知进行测试就更容易了。

哦,这是我访问使用流控制器方法的路由时终端中发生的事情的副本:

IOError (closed stream):
  /usr/local/lib/ruby/gems/2.0.0/gems/actionpack-4.0.2/lib/action_dispatch/http/response.rb:76:in `write'
  /usr/local/lib/ruby/gems/2.0.0/gems/actionpack-4.0.2/lib/action_controller/metal/live.rb:47:in `write'
  /usr/local/lib/ruby/gems/2.0.0/gems/actionpack-4.0.2/lib/action_controller/metal/live.rb:135:in `rescue in block in process'
  /usr/local/lib/ruby/gems/2.0.0/gems/actionpack-4.0.2/lib/action_controller/metal/live.rb:145:in `block in process'



IOError (closed stream):
  /usr/local/lib/ruby/gems/2.0.0/gems/actionpack-4.0.2/lib/action_dispatch/http/response.rb:76:in `write'
  /usr/local/lib/ruby/gems/2.0.0/gems/actionpack-4.0.2/lib/action_controller/metal/live.rb:47:in `write'
  /home/benjamin/Desktop/workspace/fremote/app/controllers/remotes_controller.rb:25:in `block in show'
  /usr/local/lib/ruby/gems/2.0.0/gems/activesupport-4.0.2/lib/active_support/notifications/fanout.rb:125:in `call'
  /usr/local/lib/ruby/gems/2.0.0/gems/activesupport-4.0.2/lib/active_support/notifications/fanout.rb:125:in `finish'
  /usr/local/lib/ruby/gems/2.0.0/gems/activesupport-4.0.2/lib/active_support/notifications/fanout.rb:40:in `block in finish'
  /usr/local/lib/ruby/gems/2.0.0/gems/activesupport-4.0.2/lib/active_support/notifications/fanout.rb:40:in `each'
  /usr/local/lib/ruby/gems/2.0.0/gems/activesupport-4.0.2/lib/active_support/notifications/fanout.rb:40:in `finish'
  /usr/local/lib/ruby/gems/2.0.0/gems/activesupport-4.0.2/lib/active_support/notifications/instrumenter.rb:36:in `finish'
  /usr/local/lib/ruby/gems/2.0.0/gems/activesupport-4.0.2/lib/active_support/notifications/instrumenter.rb:25:in `instrument'
  /usr/local/lib/ruby/gems/2.0.0/gems/activesupport-4.0.2/lib/active_support/notifications.rb:159:in `instrument'
  /usr/local/lib/ruby/gems/2.0.0/gems/actionpack-4.0.2/lib/action_controller/metal/instrumentation.rb:30:in `process_action'
  /usr/local/lib/ruby/gems/2.0.0/gems/actionpack-4.0.2/lib/action_controller/metal/params_wrapper.rb:245:in `process_action'
  /usr/local/lib/ruby/gems/2.0.0/gems/actionpack-4.0.2/lib/abstract_controller/base.rb:136:in `process'
  /usr/local/lib/ruby/gems/2.0.0/gems/actionpack-4.0.2/lib/abstract_controller/rendering.rb:44:in `process'
  /usr/local/lib/ruby/gems/2.0.0/gems/actionpack-4.0.2/lib/action_controller/metal/live.rb:132:in `block in process'

如果我的任何术语错误,我道歉,因为我一般都是SSE和Rails的新手,所以我非常感谢任何帮助。

编辑:如果您需要知道,我使用以下内容:

  • Rails 4.0.2
  • Ruby 2.0.0p353(2013-11-22修订版43784)[i686-linux]

1 个答案:

答案 0 :(得分:0)

这是一篇关于这个主题的好文章:http://37signals.com/svn/posts/3091-pssst-your-rails-application-has-a-secret-to-tell-you

你确定Redis和ActiveSupport :: Notifications有相同的订阅方法吗?

所以这可能是导致rails问题的原因:

  def close
    @response.commit!
    @closed = true
  end


  def write(string)
    raise IOError, "closed stream" if closed?

    @response.commit!
    @buf.push string
  end

您确保关闭了流。当写入它时,它会通过一个IOError if closed?来确保发生。我不是百分百肯定一切都在进行,但这似乎是最可能的罪魁祸首。

所以我回去看了一些东西:

在我们的响应初始化中:

  self.body, self.header, self.status = body, header, status

以下是body=

def body=(body)
  @blank = true if body == EMPTY

  if body.respond_to?(:to_path)
    @stream = body
  else
    synchronize do
      @stream = build_buffer self, munge_body_object(body)
    end
  end
end

@stream =设置为新的Buffer

def build_buffer(response, body)
  Buffer.new response, body
end

所以我认为你的罪魁祸首是你确保response.stream.close。现在,在调用之前如何调用它,我不确定。你是先做Redis,然后测试实时写入,他们是否使用相同的响应?如果是这样,您可能会将响应设置为关闭,然后尝试使用它来写入,这将通过IOerror进行。