ActionController::Live Is it possible to check if connection is still alive?
Asked Answered
L

1

18

I'm trying to implement text/event-stream using Rails 4's Live streaming. It works great and the only trouble I met is that I can't check if the connection is alive without sending any messages.

The only solution I figured out is to make supportive channel with cyclic tick generator, so that some background task will send messages there periodically. But it seems to be messy and non-reliable. Any better solutions?

Here is my controller:

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
Lurid answered 10/1, 2013 at 22:47 Comment(4)
Another option detailed here: https://mcmap.net/q/432743/-redis-actioncontroller-live-threads-not-dyingPeremptory
Looks like exactly the same as shown below, except of that it's using redis for issuing clock signal which is more complex. And after all how will it work under load? Isn't initializer fires on every request? This may overload pub/sub channel and each client will get messages from all clients.Lurid
The solution you have below is firing off an extra ticker thread per client connection. The solution I proposed in my answer I referenced has a single thread for pushing these tickers out, which scales a bit better. (And, putting it in the initializer creates one thread per rails process, not per client connection)Peremptory
I don't see the problem with one light thread per connection that do ticking in cycle. What server would you propose to use? Puma is the most multithreading one I know and even Puma has a concept of workers, so that your ticking channel will have as much messages doubles as workers/processes you have. That's my IMHO. Anyway I would suggest you to take a look at the Goliath: postrank-labs.github.io/goliathLurid
L
10

Ok, I found two options:

1) Funny but not good (as I didn't get what sever should I use to handle 1000's of parallel connections):

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) Awesome. To use Goliath server. It turned out that it can check if connection is lost without any ticker. On the way to Goliath found Cramp. It's lightweight and hopefully fast, but seems to be abandonned.

Lurid answered 12/1, 2013 at 0:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.