无法处理SSE中

时间:2016-11-15 16:35:42

标签: ruby-on-rails-4 heroku redis publish-subscribe server-sent-events

我尝试使用Redis PubSub将SSE功能添加到我的服务器应用程序,在许多文章的指导下,即: how-to-use-actioncontollerlive-along-with-resque-redis

服务器托管在Heroku中,因此心跳也是必要的。

...
sse = SSE.new(response.stream)
begin
    redis = Redis.new(:url => ENV['REDISCLOUD_URL'])
    redis.subscribe(<UUID>, HEARTBEAT_CHANNEL) do |on|
        on.message do |channel, data|
            begin
                if channel == HEARTBEAT_CHANNEL
                    sse.write('', event: "hb")
                else
                  sse.write(data, event: "offer_update")
                end
            rescue StandardError => e #I'll call this section- "internal rescue"
                puts "Internal: #{$!}"
                redis.quit
                sse.close
                puts "SSE was closed
            end
        end
    end
rescue StandardError => e  #I'll call this section- "external rescue"
    puts "External: #{$!}"
ensure
    redis.quit
    sse.close
    puts "sse was closed"
end

问题:

  1. 我没有看到&#34;内部救援&#34;在网上任何地方谈论SSE 。但如果由sse.write提出异常,我不知道谁能抓到异常?常见的情况是在客户端不再连接时发送HB,这使得该部分非常关键("Internal: client disconnected"出现)。我是对的吗?
  2. 在哪种情况下,&#34;外部救援&#34;被触发?客户端断开连接导致sse.write在&#34;内部块中引发异常&#34; (在on.message机构内)?因为当我试图模拟它几十次时它从未被外部救援抓住。
  3. 此代码也受到影响。内部救援部分中的redis.quit引发了另一个由外部救援声明捕获的异常:External: undefined method 'disconnect' for #<Redis::SubscribedClient:0x007fa8cd54b820>。那么 - 应该怎么做?我如何识别ASAP客户端断开以释放内存和插座?
  4. sse.write引发的异常怎么可能没有被外部救援抓住(因为它应该从一开始就被捕获)而另一个错误(在我的第三个问题中描述)已经被抓住了?所有这些代码(外部+内部部分)都在同一个线程中运行,对吧?我很乐意深入解释。

1 个答案:

答案 0 :(得分:0)

你在subscribe内捕获了异常,因此redis不知道它并且不会正确地停止它的内循环。 redis.quit会导致崩溃并停止,因为它无法等待消息。这显然不是一个很好的方法。

如果您的代码在subscribe内引发异常,则会导致redis正常取消订阅,并且您的例外可以在外部获救,例如在您的&#34;外部救援&#34;。

另一点是,你不应该在没有完全处理它们的情况下捕获异常,并且你不应该在不重新提升它们的情况下捕获一般异常。在这种情况下,您可以放心地将ClientDisconnected异常气泡转移到Rails&#39;代码。

以下是您的控制器代码的外观:

def subscribe
  sse = SSE.new(response.stream)
  redis = Redis.new(:url => ENV['REDISCLOUD_URL'])
  redis.subscribe(<UUID>, HEARTBEAT_CHANNEL) do |on|
    on.message do |channel, data|
      if channel == HEARTBEAT_CHANNEL
        sse.write('', event: "hb")
      else
        sse.write(data, event: "offer_update")
      end
    end
  end
ensure
  redis.quit if reddis # might not have had a chance to initialize yet
  sse.close if sse # might not have had a chance to initialize yet
  puts "sse was closed"
end