ActionController::Live:如何检查连接是否仍然存活?

18

我正在尝试使用Rails 4的实时流技术来实现文本/事件流。它工作得很好,唯一遇到的麻烦是我无法在不发送任何消息的情况下检查连接是否存活。

我想到的唯一解决方案是创建一个支持通道,带有周期性的心跳生成器,以便某些后台任务定期向那里发送消息。但这似乎很混乱且不可靠。有更好的解决方案吗?

这是我的控制器:

require 'persistency/sse'
require 'persistency/track'

class PersistencyController < ApplicationController
  include ActionController::Live

  def stream
    response.headers['Content-Type'] = 'text/event-stream'

    sse = Persistency::SSE.new(response.stream)
    track = Persistency::Track.new(current_user)
    redis = Redis.new

    begin
      redis.subscribe(:info, :chat) do |on|
        on.message do |channel, message|
          sse.write({ :message => message }, :event => channel)
        end
      end
    rescue IOError
    ensure
      track.close
      sse.close
    end
  end
end

这里还有另一种选项:https://dev59.com/0GMk5IYBdhLWcg3wywwU#19485363 - James Boutcher
看起来与下面显示的完全相同,只是它使用Redis发出更复杂的时钟信号。最终,在负载下它将如何工作?初始化器不是在每个请求上触发吗?这可能会使发布/订阅通道超载,并且每个客户端都会收到来自所有客户端的消息。 - Ivan Yurov
你下面的解决方案为每个客户端连接触发了一个额外的计时器线程。我在我的答案中提出的解决方案只有一个线程用于推送这些计时器,可以更好地进行扩展。 (并且将其放在初始化程序中会为每个Rails进程创建一个线程,而不是每个客户端连接) - James Boutcher
我认为一个连接一个轻量级线程循环执行心跳没有问题。你会选择使用哪个服务器?我知道Puma是最多线程的服务器,即使Puma也有工作进程的概念,因此你的心跳通道会随着你拥有的工作进程/进程数量增加而翻倍。这是我的观点。无论如何,我建议你看看Goliath: http://postrank-labs.github.io/goliath/ - Ivan Yurov
1个回答

10

好的,我找到了两个选项:

1) 滑稽但不好(因为我不知道应该使用哪个服务器来处理数千个并行连接):

begin
  ticker = Thread.new { loop { sse.write 0; sleep 5 } }
  sender = Thread.new do
    redis.subscribe(:info, :chat) do |on|
      on.message do |event, message|
        sse.write(message, :event => event.to_s)
      end
    end
  end
  ticker.join
  sender.join
rescue IOError
ensure
  Thread.kill(ticker) if ticker
  Thread.kill(sender) if sender
  track.close
  sse.close
end

2) 很棒。使用Goliath服务器时,它可以在没有任何时间轮的情况下检查连接是否丢失。在了解Goliath的过程中,我发现了Cramp。它很轻便并且希望速度快,但似乎已经被放弃了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接