Ruby on rails Can';t获取ActionController::Live以更好地使用ActiveSupport::Notifications

Ruby on rails Can';t获取ActionController::Live以更好地使用ActiveSupport::Notifications,ruby-on-rails,events,stream,notifications,actioncontroller,Ruby On Rails,Events,Stream,Notifications,Actioncontroller,我一直在寻找在不使用轮询的情况下使用ActionController::Live触发服务器端事件的方法。似乎唯一有文档记录的Live发布/订阅解决方案是使用Redis,但我真的希望使用我目前拥有的堆栈。我遇到了ActiveSupport::Notifications,它似乎正是我想要的 然而,当我几乎用它代替Redis时,Live抛出一个IOError,流被关闭。我看不出任何合理的理由来解释为什么会发生这种情况 例如,这应该是有效的: def stream response.header

我一直在寻找在不使用轮询的情况下使用ActionController::Live触发服务器端事件的方法。似乎唯一有文档记录的Live发布/订阅解决方案是使用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\u action.action\u controller”通知时将字符串放入控制台

当通知没有出现时,连接工作正常,但一旦有通知(我认为应该只在流中写入“hello world”),我就会得到那个愚蠢的IO错误

顺便说一下,我确实计划检测我自己的通知;仅使用现有通知进行测试更容易

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

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'
如果我弄错了任何术语,我深表歉意,因为我对SSEs和Rails一般来说都是新手,所以我非常感谢您在这方面提供的帮助

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

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

    • 这是一篇关于这个主题的好文章:

      那么,您确定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
      
      您正在确保流已关闭。调用write时,如果关闭,它将通过一个IOError
      ,您要确保它发生。我不是百分之百确定发生了什么,但这似乎是最有可能的罪魁祸首

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

      在响应的初始化中,我们有:

        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
      =设置为新的
      缓冲区

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

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

      非常感谢您的响应。我非常感谢。我想你所说的可能在一定程度上解释了我为什么会有问题。但在回答您的问题时,我发现通知的订阅方法的行为与Redis有点不同。同样,我没有使用Redis,但在我在其上看到的视频中,subscribe方法似乎像一个循环,而通知只是订阅并继续。通过在ActiveSupport订阅下添加一个sleep命令,我可以发送“HelloWorld”。我计划很快发布我的解决方案。我从来不喜欢用睡眠作为解决方案。您使用的是像thin或puma这样的线程服务器吗?如果你使用的是独角兽,你会想切换到其中一个。这些评论有一些很好的解释:是的,我用的是彪马。我已经得到了我的解决方案,只要睡眠是定时的,而不是无限期的,以允许旧线程死亡。这不太好(仍在寻找更好的方法来杀死线程),但我看不出使用sleep有什么特别糟糕的地方,因为Redis基本上做了相同的事情(在触发订阅之前阻塞,然后继续阻塞)。我已经考虑过您发布的解决方案,但我使用的是MongoDB。它可能有一个类似的特性,尽管我更喜欢数据库无关的东西。然而,这些评论仍然有一些不错的信息。