I'm building an application for a web-based slide show, where one 'master' user can move between slides and everyone's browsers follow along. To do this, I'm using websockets and Redis for a global channel to send messages through. Each client who connects has there info stored in an array, @clients
.Then I have a separate thread for subscribing to the Redis channel, in which there is an 'on.message' block defined which should send a message to everyone in the @clients
array, but that array is empty inside this block (not empty anywhere else in the module).
Pretty much following this example:https://devcenter.heroku.com/articles/ruby-websockets
The relevant code, which is in a custom middleware class:
require 'faye/websocket'require 'redis'class WsCommunication KEEPALIVE_TIME = 15 #seconds CHANNEL = 'vip-deck' def initialize(app) @app = app @clients = [] uri = URI.parse(ENV['REDISCLOUD_URL']) Thread.new do redis_sub = Redis.new(host: uri.host, port: uri.port, password: uri.password) redis_sub.subscribe(CHANNEL) do |on| on.message do |channel, msg| puts @clients.count ### prints '0,' no clients receive msg @clients.each { |ws| ws.send(msg) } end end end end def call(env) if Faye::WebSocket.websocket?(env) ws = Faye::WebSocket.new(env, nil, {ping: KEEPALIVE_TIME}) ws.on :open do |event| @clients << ws puts @clients.count ### prints actual number of clients end ws.on :message do |event| $redis.publish(CHANNEL, event.data) end ws.on :close do |event| @clients.delete(ws) ws = nil end ws.rack_response else @app.call(env) endendend
Is the @clients
array empty when accessed inside the new thread because instance variables aren't shared across threads? and if so, how do I share a variable across threads?
I have also tried using $clients (global variable, should be accessible across threads), to no avail.