facebook

Blog

Stay updated

Redis is not only an excellent key-value store for caching but can also be used as a message broker: let's see how!
“Red(e)isign” Publish/Subscribe with Redis
Wednesday, September 16, 2020

I recently had the opportunity to work for one of our customers with an application using Redis. Talking about it with colleagues and studying the documentation, I discovered that this tool could also be used to decouple communication in a software system. Driven by curiosity, I tried to understand better what it was and how I could use it as a messaging system.

Redis, named after Remote Dictionary Server, is defined as an in-memory data store. It is a key-value store type database, thus the data storage requires a key through which value is identified within the database. In the case of Redis, different types are supported for the values ​​associated with the keys such as strings but also structured data types such as lists and sets.

To experiment with Redis, I used a Docker container created with the following command:

docker run –name redis-pubsub -p 6379:6379 -d redis

Let’s see a simple example of how we can save key-value information on Redis using the redis-cli, a potent tool supplied with the product. In particular, the SET and GET commands to save and obtain the value saved using the key:

The most widespread use of Redis is as a database or as a cache, mainly when there is a need to access simple data quickly, thus exploiting the speed of the central memory (however, the possibility of persisting data is still provided).

From version 2.0, new commands have been introduced for the redis-cli that allow you to create the publish/subscribe pattern. I have talked extensively about this pattern in previous articles (i.e., this one) and then we will focus on how it can be achieved through Redis.

It is expected that a Subscriber interested in a specific topic can subscribe to the related Channel while waiting for a Publisher to publish information on that channel. Whether they are publishers or subscribers, clients connect to Redis via TCP connection and communicate with the Redis Server via the RESP protocol.

Redis uses a hash table allocated in memory to keep track of channel names and their subscribers. When a new subscriber wants to subscribe to the channel, the latter is hashed to identify the subscribers’ list, and the new one is added to this. Similarly, in the case of publishing, the list of subscribers of a channel is retrieved and the message will be delivered for each of them.

To show an example of using this Redis feature, I created a simple project in which we have a client (FeedRssClient), A user can request the subscription to an RSS feed and a FeedRssPublisher that receives the feed URL from the client elaborates it, and publishes its contents.

To do this I used two application consoles in .NET Core. As a Redis client I used the StackExchange.Redis library available as a Nuget package.

Let’s start with FeedRssClient:

using StackExchange.Redis;
using System;
 
namespace FeedRssClient
{
    class Program
    {
        private const string RedisConnectionString = "localhost";
        private static ConnectionMultiplexer connection = ConnectionMultiplexer.Connect(RedisConnectionString);
        private const string publishChannel = "client-channel";
        private const string subscriberChannel = "rss-channel";
        private static string feedUrl = string.Empty;
 
        static void Main()
        {
            Console.WriteLine("FeedRssClient\r\n");
            Console.WriteLine($"Please enter the RSS feed that you want to follow: ");
            feedUrl = Console.ReadLine();
 
            var pubSubConnection = connection.GetSubscriber();
 
            pubSubConnection.Publish(publishChannel, $"{feedUrl}");
            var subscriberChannelMessageQueue = pubSubConnection.Subscribe(subscriberChannel);
            Console.WriteLine($"List of content: \r\n");
            subscriberChannelMessageQueue.OnMessage(message =>
            {
                Console.WriteLine(message);
            });
            Console.ReadLine();
        }
    }
}

First, we establish a connection to the Redis container created previously through the Connect() method of the ConnectionMultiplexer. The connection multiplexer is a fundamental element provided by the StackExchange library that allows you to access the Redis database or take advantage of the publish/subscribe features.

Let’s define two channels: the first one, the client-channel, will be the channel where the FeedRssClient will publish the address of the RSS feed to which it wants to subscribe; the second one, the rss-channel, is the channel to which the FeedRssClient will subscribe.

The OnMessage() method of the subscription channel ensures that the received messages will be read sequentially.

If there is no need to receive the messages in order, you can also use the concurrent version of this method, which is much more efficient if the messages do not correlate with each other.

Let’s now see the FeedRssPublisher:

using Microsoft.Toolkit.Parsers.Rss;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
 
namespace FeedRssPublisher
{
    class Program
    {
        private const string RedisConnectionString = "localhost";
        private static ConnectionMultiplexer connection = ConnectionMultiplexer.Connect(RedisConnectionString);
        private const string publishChannel = "rss-channel";
        private const string subscriberChannel = "client-channel";
 
        static void Main()
        {
            Console.WriteLine("FeedRssPublisher\r\n");
            var feed = new Feed();
            var pubSubConnection = connection.GetSubscriber();
            var feedUrl = string.Empty;
            var subscriberChannelMessageQueue = pubSubConnection.Subscribe(subscriberChannel);
 
            subscriberChannelMessageQueue.OnMessage(async message =>
            {
                feedUrl = message.ToString().Remove(0, subscriberChannel.Length + 1);
                var rss = await feed.ParseRSSAsync(feedUrl);
                Console.WriteLine($"Feed Received: {feedUrl}\r\n");
                if (rss != null)
                {
                    Console.WriteLine("Start publishing contents ...");
                    foreach (var item in rss)
                    {
                        pubSubConnection.Publish(publishChannel, $"{item.Title}" + $"\r\n{item.Summary}" + $"\r\n{item.FeedUrl}\r\n");
                    }
                }
            });
 
            Console.ReadLine();
 
        }
 
        class Feed
        {
            public async Task<IEnumerable<RssSchema>> ParseRSSAsync(string feed)
            {
                IEnumerable<RssSchema> rss = null;
 
                using (var client = new HttpClient())
                {
                    try
                    {
                        feed = await client.GetStringAsync(feed);
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                }
 
                if (feed != null)
                {
                    var parser = new RssParser();
                    rss = parser.Parse(feed);
                }
 
                return rss;
            }
        }
    }
}

Here too, we create a connection to the Redis container and define the two channels whose roles, in this case, will be inverted compared to the FeedRssClient.

In fact, the FeedRssPublisher will publish the messages on the rss-channel while it will subscribe to the client-channel channel.

Immediately after the subscription, the RSS feeds are extracted within the OnMessage() method using the Feed class, and subsequently, for each feed, we publish the title, the summary, and the URL.

Let’s try the two software with the Rss feed of our blog https://www.blexin.com/rssfeed/articles?lang=en-US:

The publisher receives the URL of the RSS feed and starts publishing its contents:

The client receives the list of contents:

Of course, we can also use the redis-cli to subscribe to the rss-channel:

As we have seen from the example, it is very simple to create the publish/subscribe pattern with Redis, and obtain a decoupled software system.

The use of Redis allows us to obtain a lean and efficient messaging system both for the in-memory nature of Redis and for scalability or the ability to configure it in a distributed manner.

For example, the Pub/Sub mechanism is widely used in real-time applications. Consider, for example, chat rooms or those application scenarios in which you need to collect a large amount of data (data ingestion) and process these large volumes at high speed (stream processing). Another interesting example that we will see in a future article is using Redis to scale ASP.NET Core SignalR applications.

However, it must be considered that this mechanism is of the fire & forget type and therefore does not provide any persistence for messages with the consequent impossibility of recovering messages in case of a client disconnection.

I hope this Redis feature just described will be useful and interesting.

I leave you the link of the GitHub repository where you can find the code used for the example and the references used to create the article:

https://redis.io/topics/pubsub

https://making.pusher.com/redis-pubsub-under-the-hood

See you in the next article!