Redis pub/sub on rails

2020-02-23 08:46发布

Following the Redis Pub/Sub

this works fine and i can publish messages in any class using

$redis.publish 'channel', { object: @object.id }

using redis-cli > MONITOR, I can verify that this request was published correctly

[0 127.0.0.1:64192] "publish" "channel" "{:object=>\"5331d541f4eec77185000003\" }"

the problem starts when I add a subscriber block to that channel in other class (listener class) like the following

class OtherClass
  $redis.subscribe('channel') do |payload|
    p payload
  end
end

in redis-cli > MONITOR, shows also that the listener is subscribed correctly

[0 127.0.0.1:52930] "subscribe" "channel"

the problem is that when i add the subscriber listener class to the same rails app... it stop working cause the OtherClass listens to the redis server and halt the execution of any other code... it just sit there listening.

so is there a way to make a messaging bus with redis on the same rails app... so that events are published from some classes or service objects and there are listeners for specific channels to act upon receiving events in the background.

i know that i might use sidekiq or any other background worker to do this job... but after a while the background workers became messy and unmaintainable.

1条回答
啃猪蹄的小仙女
2楼-- · 2020-02-23 09:13

The implementation of Redis#subscribe is a loop that will assume control of the current thread in order to listen to events. This means the boot process is halted when dropping a subscription into the context of a Rails class in the way you've shown.

You could try wrapping the call in a thread, but this approach would literally create a new subscription each time this class loaded in a new process, like a rails console or multiple unicorns. Plus, you'd have to be careful about shared state and other threading issues. This is probably not what you want.

You're best off starting a different process that loads the rails environment and subscribes to redis separately from the process(es) serving web requests. It could be a rake task like the following:

namespace :subscribe do
  task :redis => :environment do
    $redis.subscribe("bravo") do |on|
      on.message do |channel, message|
        Rails.logger.info("Broadcast on channel #{channel}: #{message}")
        OtherClass.some_method # yada yada
      end
    end
  end
end
查看更多
登录 后发表回答