Rails实例变量不在线程中更新

时间:2015-09-21 23:09:14

标签: ruby-on-rails multithreading

我正在使用websockets,redis和threads进行基本的聊天设置。我在线程启动之前创建一个客户端数组,然后更新客户端数组。但是,线程中永远不会更新客户端数组。因此,当我尝试发布到连接的客户端时,没有发布任何内容。我正在处理example directly on Heroku并且无法弄清楚客户端数组没有更新的原因。

这是我的代码:

class ChatBackend
  KEEPALIVE_TIME = 15 # in seconds
  CHANNEL        = "chat-demo"


  def initialize(app)
    @app     = app
    @clients = []

    uri = URI.parse(ENV["REDISCLOUD_URL"])
    @redis = Redis.new(host: uri.host, port: uri.port, password: uri.password)
    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|
            # @clients never gets updated, always stays empty
          @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
      end

      ws.on :message do |event|        
        @redis.publish(CHANNEL, prepare(event.data))
      end

      ws.on :close do |event|
        @clients.delete(ws)
        ws = nil
      end

      # Return async Rack response
      ws.rack_response

    else
      @app.call(env)
    end
  end

  private
  def prepare(data)
    json = JSON.parse(message)
    json.each {|key, value| json[key] = ERB::Util.html_escape(value) }
    JSON.generate(json)    
  end
end

1 个答案:

答案 0 :(得分:0)

这花了我一整天的时间来确定,但它包含在Heroku示例here的wiki问题中:

  

指定工作人员将puma置于聚集模式,并稍微打破了我   本例中提供的中间件的修改版本。

     

我确认消息正在正确发布到redis   pubsub通道,以及线程内部的redis订阅   确实收到消息并触发on.message块,但是ws   浏览器中的客户端永远不会收到消息。

     

这是因为@clients在线程内是一个空数组(对于a   这是逃避我的原因),意味着该块传递给了   永远不会执行@ clients.each,因此消息就会消失。

     

在该线程之外访问@clients会返回所有打开的内容   ws连接正确。

     

删除worker 3行修复了这个问题,但我想我会这样做   报告它,以防其他人自己碰到这个。