Pub Sub Across Servers Using Redis

11 בספטמבר 2013

no comments

Redis is famous key value store that uses memory as its primary storage. This is why it is known for its speed and is often being used as a distributed cache in distributed systems.

One important feature of Redis is its pub/sub system. With Redis Pub/Sub, processes can communicate and implement the publish subscribe pattern (one process publishes a message and many other can listen). More on Redis pub/sub can be found here.

The question I’d like to discuss is how to do this across servers.
When all participants are connected to the same Redis host Pub/Sub is trivial, yet in distributed systems it makes sense that this assumption is false (The publisher is connected to one Redis host and some subscribers are connected to other hosts)

Is it possible to implement pub/sub across servers?

The answer is: Yes but with some limitations.

The idea is to user Redis Replication.
Replication is very simple to use. It configures a master-slave replication that allows slave Redis servers to be exact copies of their master servers. All you have to do to establish such replication is add a simple line of configuration in the slave redis.conf file.

//slaveof serverName portNumber
slaveof localhost 6379

With replication in-place the  publisher can publish in the master host and the subscribers can subscribe to the slave host.

It is important to mention that this relationship is one-way. Master –> Slave relationship are unidirectional. It is impossible to publish to the slave and subscribe to the master.

Let us see a small sample:

I created two Redis hosts: One on port 6379 and the other on port 6380. The second host is slave of the first (slaveof localhost 6379) 

static void PublishInOneServer(int port)
{
            var messagesReceived = 0;
            int PublishMessageCount = 100;
            string MessagePrefix = "hello";
            string ChannelName = "MyChannel";

            using (var redisPublisher = new RedisClient("localhost", port))
            {
                for (var i = 1; i <= PublishMessageCount; i++)
                {
                    var message = MessagePrefix + i;
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("Publishing '{0}' to '{1}'", message, ChannelName);
                    Console.ForegroundColor = ConsoleColor.White;

                    redisPublisher.PublishMessage(ChannelName, message);
                }
            }
}

static void SubscribeInOtherServer(int port)
{
            var messagesReceived = 0;
            int PublishMessageCount = 100;
            string ChannelName = "MyChannel";

            using (var redisConsumer = new RedisClient("localhost", port))
            using (var subscription = redisConsumer.CreateSubscription())
            {

                subscription.OnSubscribe = channel =>
                                               {
                                                   Console.ForegroundColor = ConsoleColor.Green;
                                                   Console.WriteLine("Subscribed to '{0}'", channel);
                                                   Console.ForegroundColor = ConsoleColor.White;
                                               };
                subscription.OnUnSubscribe = channel =>
                                                 {
                                                     Console.WriteLine("UnSubscribed from '{0}'", channel);
                                                 };
                subscription.OnMessage = (channel, msg) =>
                                             {
                                                 Console.ForegroundColor = ConsoleColor.Yellow;
                                                 Console.WriteLine("Received '{0}' from channel '{1}'", msg, channel);
                                                 Console.ForegroundColor = ConsoleColor.White;


                 //As soon as we've received all 5 messages, disconnect by unsubscribing to all channels
                                                 if (++messagesReceived == PublishMessageCount)
                                                 {
                                                     subscription.UnSubscribeFromAllChannels();
                                                 }
                                             };

                Console.WriteLine("Started Listening On '{0}'", ChannelName);
                subscription.SubscribeToChannels(ChannelName); //blocking
            }
 }

The following will work great.

Task.Run(()=>SubscribeInOtherServer(6379));
Task.Run(()=>PublishInOneServer(6380));

but when we switch direction the pub/sub will fail.

Task.Run(() => SubscribeInOtherServer(6380));
Task.Run(() => PublishInOneServer(6379));

The conclusion is simple: Two ways pub/sub channels across servers require at least 4 Redis hosts.

Hope this helps

Manu

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*