Introduction
This article is for beginners in Redis Pub/Sub. Actually, Redis itself from its pub/sub documentation has a nice sample chat app written in Ruby here (try check it out), but in this article, we are going to learn about Redis pub/sub and create a very simple chat app (55 lines of code including newlines and comments) using C# (.NET Core) and Redis, we'll see the things that we need to consider from this application as well later.
Background
Redis is an open source, in-memory data structure store, used as a database, cache and message broker. It's one of NoSQL databases. Basically, it's a key/value store with some other powerful features (one of them is pub/sub).
Requirements
In order to follow this article and make our hands dirty with the code, we need to have:
-
Redis. We can have Redis server ready from Microsoft Azure or Amazon Web Services, or if you don't want to sign up to any cloud service, you can install Redis server on your computer. This also assumes that you already know how to use -at least the basic of- redis-cli
.
-
.NET Core. As we know, .NET Core runs on Windows, Linux, and macOS / OS X.
-
Source Code Editor or IDE. We need this of course, but for Windows (or Mac), it's recommended to use Visual Studio. We can use other source code editor other than Visual Studio if we want as long as we have .NET Core installed.
Redis Pub/Sub Basic
Citing from Wikipedia, publish–subscribe is a messaging pattern where senders of messages, called publishers, do not program the messages to be sent directly to specific receivers, called subscribers, but instead categorize published messages into classes without knowledge of which subscribers, if any, there may be. Similarly, subscribers express interest in one or more classes and only receive messages that are of interest, without knowledge of which publishers, if any, there are.
As we mentioned before, Redis is not only key/value server, but it's also a messaging server. With Redis, we can do publish-subscribe. Redis has a good documentation for its pub/sub (or publish-subscribe) feature here. But in simple sentence, Redis publish-subscribe enables us to do quick messaging and communication between processes.
It needs to be mentioned that Redis pub/sub has no relation to the key space. It was made to not interfere with it on any level, including database numbers.
Redis has simple commands to enable publish-subscribe. In Redis CLI, the command to subscribe to a channel is pretty simple.
SUBSCRIBE channel_one channel_two
In the example above, client subscribes to channels with name channel_one
and channel_two
. A client can subscribe to more than one channel.
If we open another client (redis-cli
or other interactive tool for Redis) without closing the other client for subscribing and try PUBLISH
command like this.
PUBLISH channel_one "Hey Guys!"
The message will be heard to the subscribers. We can see the example below (gif).
Alright, that's for the basic. Actually, Redis has other commands related with pub/sub, they are UNSUBSCRIBE
for unsubscribing channel(s), PSUBSCRIBE
for pattern matching subscription for channel name (using glob-style pattern), and PUNSUBSCRIBE
. Let's go to our very simple chat app.
Creating Simple Chat App
In this section, we are going to create a very simple chat app using C# with only 55 lines of code. This article assumes you're using Visual Studio.
Let's begin. First things first, create a console app using .NET Core, you can name it "SimpleChatApp
". After that, we need to get/install this nuget package: StackExchange.Redis to communicate with Redis, this is Redis client library for .NET Languages (C#, etc.), I recommend you to see the documentations.
We'll do it step by step. First step, we need to create the connection to Redis.
using StackExchange.Redis;
using System;
namespace SimpleChatApp
{
class Program
{
private const string RedisConnectionString = "localhost";
private static ConnectionMultiplexer connection =
ConnectionMultiplexer.Connect(RedisConnectionString);
private const string ChatChannel =
"Chat-Simple-Channel";
private static string userName = string.Empty;
static void Main()
{
Console.WriteLine("Hello World!");
}
}
}
It's good to know that ConnectionMultiplexer
implements IDisposable
, normally we need to reuse the object for the entire application lifetime. In the above code, we don't use using
block, because after we close the console application, it will be disposed automatically.
For the connection string configuration, instead of using localhost, we can use Microsoft Azure Cache or other cloud service for Redis. For the configuration documentation, we can check it here.
Let's continue. In Main()
method, write this code:
static void Main()
{
Console.Write("Enter your name: ");
userName = Console.ReadLine();
var pubsub = connection.GetSubscriber();
pubsub.Subscribe(ChatChannel, (channel, message) => MessageAction(message));
pubsub.Publish(ChatChannel, $"'{userName}' joined the chat room.");
while (true)
{
pubsub.Publish(ChatChannel, $"{userName}: {Console.ReadLine()} " +
$"({DateTime.Now.Hour}:{DateTime.Now.Minute})");
}
}
private static void MessageAction(RedisValue message)
{
throw new NotImplementedException();
}
We can see that from the code above, in order to subscribe, we need to pass the channel name as the first parameter and then the handler/callback as the second parameter. For the second parameter, we pass Action with two parameter, the channel and the message. For the action, we create MessageAction
. We don't need channel object here since we are only concerned about the message. For publishing the message, just like PUBLISH
command before, we need to pass the channel name and then the message itself.
We use MessageAction
to print the message, let's write our method with the code below:
static void MessageAction(string message)
{
int initialCursorTop = Console.CursorTop;
int initialCursorLeft = Console.CursorLeft;
Console.MoveBufferArea(0, initialCursorTop, Console.WindowWidth,
1, 0, initialCursorTop + 1);
Console.CursorTop = initialCursorTop;
Console.CursorLeft = 0;
Console.WriteLine(message);
Console.CursorTop = initialCursorTop + 1;
Console.CursorLeft = initialCursorLeft;
}
Let's put it all together, the code will look like this:
using StackExchange.Redis;
using System;
namespace SimpleChatApp
{
class Program
{
private const string RedisConnectionString = "localhost";
private static ConnectionMultiplexer connection =
ConnectionMultiplexer.Connect(RedisConnectionString);
private const string ChatChannel = "Chat-Simple-Channel";
private static string userName = string.Empty;
static void Main()
{
Console.Write("Enter your name: ");
userName = Console.ReadLine();
var pubsub = connection.GetSubscriber();
pubsub.Subscribe(ChatChannel,
(channel, message) => MessageAction(message));
pubsub.Publish(ChatChannel, $"'{userName}' joined the chat room.");
while (true)
{
pubsub.Publish(ChatChannel, $"{userName}: {Console.ReadLine()} " +
$"({DateTime.Now.Hour}:{DateTime.Now.Minute})");
}
}
static void MessageAction(string message)
{
int initialCursorTop = Console.CursorTop;
int initialCursorLeft = Console.CursorLeft;
Console.MoveBufferArea(0, initialCursorTop, Console.WindowWidth,
1, 0, initialCursorTop + 1);
Console.CursorTop = initialCursorTop;
Console.CursorLeft = 0;
Console.WriteLine(message);
Console.CursorTop = initialCursorTop + 1;
Console.CursorLeft = initialCursorLeft;
}
}
}
That's it! Build the project.
Note that if we use .NET Core, we can run the application using this command (in the output DLL directory, bin/Debug or bin/Release) using Command Prompt or Terminal if you're using Linux:
dotnet SimpleChatApp.dll
Let's see how it works.
We've created a chat app, just for fun! As long as it's connected to Redis (or Redis in the cloud service), we can chat.
Things We Need to Consider
We need to know that with this application, Redis connections are opened/created per application by default, and it's not ideal. If we use Redis from Microsoft Azure service or some other cloud services for Redis, there is a maximum number of client connections there.
It's also good to mention that StackExchange.Redis
client library by default opens two connections to Redis, one for interactive command, and one for pub/sub. So, this application creates two connections to Redis (per application). If we have Redis installed, we can see the statistics of Redis connections using this command:
redis-cli --stat
Remember, this is just a very simple chat app, but we can still use it for fun knowing the caveats. It would be better if we use client-server application, and host it somewhere.
Have fun! :)
History
- 21st December, 2017: Initial version