Redis Pub Sub, somehow may sound weird that it provides such a functionality since when you think of Redis the first thing that comes to mind is of a cache key value store. But indeed Redis does allow us to use the messaging paradigm by using channels to publish messages and for subscribers to listen for notification of the message. Redis Pub Sub allows multiple subscribers to listen to one or more channels, and to only receive messages that are of interest. It decouples the subscriber from the publisher since the subscriber has no knowledge of whom the publishers are and vice versa there could be multiple publisher not knowing whom the subscribers are.
Sample Diagram of Redis Pub Sub
There are definitely certain restrictions of using Redis Pub Sub as a Messaging System, it will not be like RabbitMQ, Kafka or Azure MessageBus etc. Those message bus are able to store the message for durability or even replay of an old message for consumption. Redis uses a listener model where there are no listeners (subscribers) it will not receive those messages. But if you wish to have a simple pub sub without the heavy tools then Redis does quite a good job at it.
Redis Pub Sub – Operations
- PUBLISH channels message: Posts a message to the given channel O(N+M)
- SUBSCRIBE [channel]: Subscribe to a given channel for message, O(N) where N is the number of channels to subscribe to
- PSUBSCRIBE [channel]: Subscribes the client to the given patterns, O(N) where N is the number of patterns the client is already subscribed to.
- PUBSUB CHANNELS pattern: Currently active channels, O(N)
- PUBSUB NUMSUB channel: Number of subscribers to the channels provided, O(N)
- PUBSUB NUMPAT: Number of subscriptions to all the patterns O(N)
- PUNSUBSCRIBE: Unsubscribes the client from a pattern, O(N+M)
- UNSUBSCRIBE: Unsubscribes the client from a channel, O(N) where N is the number of clients already subscribed to a channel.
C# code using Redis PubSub
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
class Program { static void Main(string[] args) { var redis = RedisStore.RedisCache; var sub = redis.Multiplexer.GetSubscriber(); //first subscribe, until we publish //subscribe to a test message sub.Subscribe("test", (channel, message) => { Console.WriteLine("Got notification: " + (string)message); }); //create a publisher var pub = redis.Multiplexer.GetSubscriber(); //pubish to test channel a message var count = pub.Publish("test", "Hello there I am a test message"); Console.WriteLine($"Number of listeners for test {count}"); //pattern match with a message sub.Subscribe(new RedisChannel("a*c", RedisChannel.PatternMode.Pattern), (channel, message) => { Console.WriteLine($"Got pattern a*c notification: {message}"); }); count = pub.Publish("a*c", "Hello there I am a a*c message"); Console.WriteLine($"Number of listeners for a*c {count}"); pub.Publish("abc", "Hello there I am a abc message"); pub.Publish("a1234567890c", "Hello there I am a a1234567890c message"); pub.Publish("ab", "Hello I am a lost message"); //this mesage is never printed //Never a pattern match with a message sub.Subscribe(new RedisChannel("*123", RedisChannel.PatternMode.Literal), (channel, message) => { Console.WriteLine($"Got Literal pattern *123 notification: {message}"); }); pub.Publish("*123", "Hello there I am a *123 message"); pub.Publish("a123", "Hello there I am a a123 message"); //message is never received due to literal pattern //Auto pattern match with a message sub.Subscribe(new RedisChannel("zyx*", RedisChannel.PatternMode.Auto), (channel, message) => { Console.WriteLine($"Got Literal pattern zyx* notification: {message}"); }); pub.Publish("zyxabc", "Hello there I am a zyxabc message"); pub.Publish("zyx1234", "Hello there I am a zyxabc message"); //no message being published to it so it will not receive any previous messages sub.Subscribe("test", (channel, message) => { Console.WriteLine($"I am a late subscriber Got notification: {message}"); }); sub.Unsubscribe("a*c"); count = pub.Publish("abc", "Hello there I am a abc message"); //no one listening anymore Console.WriteLine($"Number of listeners for a*c {count}"); Console.ReadKey(); } } |
So this covers the basic usage of Redis PubSub, in the next blog post I will cover how to use Pipelines in Redis.
For the code please visit
https://github.com/taswar/RedisForNetDevelopers/tree/master/9.RedisPubSub
For previous Redis topics
- Intro to Redis for .NET Developers
- Redis for .NET Developer – Connecting with C#
- Redis for .NET Developer – String Datatype
- Redis for .NET Developer – String Datatype part 2
- Redis for .NET Developer – Hash Datatype
- Redis for .NET Developer – List Datatype
- Redis for .NET Developer – Redis Sets Datatype
- Redis for .NET Developer – Redis Sorted Sets Datatype
- Redis for .NET Developer – Redis Hyperloglog
Hi,
How can I create an application that connect to Redis, config a subscriber, sleep and wakeup on received message, could I give some direction please?
Just wondering why would you want the application to go to sleep? If you use pub/sub the application should not be consuming much resources, just like any notification applications that runs either in windows. But if you really want sleep and wake a thread up here is a way to do it. Look at
https://msdn.microsoft.com/en-us/library/system.threading.eventwaithandle.aspx
An EventWaitHandle blocks until it is signaled to wake up by the Set() method. In your case it will be signaled by the Redis subscriber event.
private void DoStuff()
{
// do some stuff
myWaitHandle.WaitOne(); // this will block until
// continue thread
}
private void SubscriberMessageReceivedByRedis()
{
myWaitHandle.Set(); // this signals the wait handle and your other thread will continue
}
I am fortunate to see this example snippet so I have choosen StackExchange.Redis instead of ServiceStack.Redis which I just found out a while ago that it has an expiry after you reach a certain amount of GET/SET operations. That would have been a sitting timebomb for my deployment. DO NOT use ServiceStack.Redis!!!