Rails Unicorn 里创建线程

ming_relax · 2015年03月23日 · 最后由 ming_relax 回复于 2015年03月29日 · 2469 次阅读

我想要在 Rails 里创建一个线程,这个线程用来监听 Redis 里的消息。我 Unicorn 作为我的 web 服务器。我试过在 unicorn 的配置文件里用这种方法:

after_fork do |server, worker|

  Thread.new do
    begin
      $redis.subscribe(:one, :two) do |on|
        on.subscribe do |channel, subscriptions|
          puts "Subscribed to ##{channel} (#{subscriptions} subscriptions)"
        end
        on.message do |channel, message|
          puts "##{channel}: #{message}"
          $redis.unsubscribe if message == "exit"
        end
        on.unsubscribe do |channel, subscriptions|
          puts "Unsubscribed from ##{channel} (#{subscriptions} subscriptions)"
        end
      end
    rescue Redis::BaseConnectionError => error
      puts "#{error}, retrying in 1s"
      sleep 1
      retry
    end
  end
end

但是这样的话整个 unicorn 还是阻塞了,无法响应客户端请求。 我这样用是不是有什么问题?

Don't do this, please use job process!

Sidekiq or Resque

#1 楼 @lyfi2003 我是想在 web process 里面起一个线程监听 redis 的事件,和 sidekiq 这样的用途不一样。比如,我想根据 redis 消息清掉当前 web process 里的某一块数据结构。

#2 楼 @ming_relax 那你应该自己写一个守护进程监听后,修改一个状态值,然后让 Web Process 去检查状态值,而不是反过来作

Ruby 线程有 GIL,可能与这个有关。

@Rei 是的,就是 GIL 的问题,我问了 unicorn 社区。而且不用 unicorn,只用简单的几个线程 subscribe redis,同样有这样的问题。

#2 楼 @ming_relax 如果想做到“想根据 redis 消息清掉当前 web process 里的某一块数据结构”,那么这个数据结构为什么不放在 redis 里。

@ming_relax 根据你的情况,感觉找一个能结合 Rails 和 Redis 的 pub/sub 的 gem 是一个可行的方向。因为你需要 pub/sub 的机制来获取通知,这不适合用 job 或者定时任务去做。我大概用 "rails redis pub sub" 搜了下,有个方法 是把 redis 的代码放到 rake task 里面去。这个还比较可行。

或者你也可以放弃 redis 的 pub/sub,改用定时任务去不停轮询。即时度没那么高,但应该也够用。

或者尝试 JRuby?因为它没有 GIL 限制。不过踩坑估计更多。而且直接操作 thread 的代码维护成本比较高。edge case 也要注意。

@ylt 放在 memcached 或者 redis 里各个进程共享数据也是一个可行的方案,我是想让访问速度更快,所以看有没有什么方案可以参考。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号