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.
Aside from data storage, Redis can be used as a Publisher/Subscriber platform. In this pattern, publishers can issue messages to any number of subscribers on a channel. These messages are fire-and-forget, in that if a message is published and no subscribers exists, the message evaporates and cannot be recovered.
Basic Pub-Sub modelStart by opening three terminal sessions and launch the Redis CLI in each of them. Once you have all the terminals open and set up, use one of the terminals to SUBSCRIBE to a channel. The name will entirely depend on you, and you can name it whatever you want.
Redis' pub/sub sends messages to clients subscribed (listening) on a channel. If you are not listening, you will miss the message (hence the blocking call).
Pub/Sub is synchronous communication (push protocol). All parties need to be active at the same time to be able to communicate. Here Redis is a pure synchronous messaging broker.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With