Redis Pub/Sub: Howto Guide

Our overview article on Redis pub/sub discusses the purpose of pub/sub and describes the design choices of Redis pub/sub in particular. We’ll now turn to how to use Redis pub/sub by stepping through each of the main concepts of Redis pub/sub: channels, publishing, subscribing, and pattern-matching, using the noderedis node.js client.

Understanding channels

A channel is a name used to categorize messages published on the pub/sub system. Channels can have system-dependent names, like system-health:i-36a44b83, trade-prices:RAX, temp-reading:living-room, or something very generic, like events. Any subscriber interested in the data that appears on a channel can listen to it, and new publishers and subscribers are easily added as the system grows.

To find out which channels are active on a Redis server, you can use the PUBSUB CHANNELS command, which returns nothing when a system is not yet being used for pub/sub:

$ redis-cli pubsub channels
(empty list or set)

Channels exist on the system only when a subscriber is listening to messages on it, so there is no need to ever “create” or “remove” channels – they exist only while a subscriber is listening. To see this, we can use redis-cli to act as a subscriber in one console window, and run PUBSUB CHANNELS again in another window:

Console 1:

$ redis-cli subscribe events
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "events"
3) (integer) 1

Console 2:

$ redis-cli pubsub channels
1) "events"

As soon as Console 1 disconnects the system will again have no channels.

Publishing messages

Command Syntax

The PUBLISH command is used to publish messages.

PUBLISH channel message

All subscribers listening on the channel will be sent the message. If no subscribers are around to receive the message the message is dropped.

Node.js example

After creating a regular connection to Redis, publish can be used like any other command:

var client = require("redis").createClient();
client.publish("temp-reading:living-room", "37.0");

Using with other Redis commands

Publishing data to a channel is a fast operation, so it’s commonly used in tandem with other operations. Combining operations like this unlocks the power of Redis:

var client = require("redis").createClient();

client.multi()
  .publish("temp-reading:living-room", "37.0")
  .lpush("recent-temperatures", "37.0")
  .ltrim("recent-temperatures", 0, 99)
  .exec();

This is the same operation as the earlier example for the pub/sub system, but in the same MULTI/EXEC transaction the temperature is also pushed onto a list which keeps the 100 most recent temperatures.

This short example shows a way to integrate Redis pub/sub into a larger system design that includes message history. An operation like the one above lets other clients query recent values as well as subscribe to new ones.

Subscribing to messages

Command Syntax

The SUBSCRIBE command is used to subscribe to channels. This command puts the client in a special “subscribed” state where it no longer sends commands other than additional SUBSCRIBE or UNSUBSCRIBE commands.

SUBSCRIBE channel [channel ...]

Node.js example

This subscriber could be used with the earlier publisher example:

var subscriber = require("redis").createClient();

subscriber.on("message", function(channel, message) {
  console.log("A temperature of " + message + " was read.");
});

subscriber.subscribe("temp-reading:living-room");

This uses a simple event emitter which calls a function every time a message arrives. The handling function just logs the temperature to the console right now – it could do anything.

Using other commands with subscribers

As mentioned earlier, subscribed clients are in a special mode and cannot be used for other commands. To use other commands, you’ll create a separate client connection to Redis:

var redis = require("redis")
  , subscriber = redis.createClient()
  , client = redis.createClient();

subscriber.on("message", function(channel, message) {
  console.log("A temperature of " + message + " was read.");
  client.incr("temp-count");
});

subscriber.subscribe("temp-reading:living-room");

This handler logs the message to the console and increments a Redis counter. In some cases you might calculate a result and then publish that result to a separate channel for other subscribers to read – just don’t publish it back to the same channel you’re listening on, as you’ll cause an infinite loop :)

Pattern matching

Command Syntax

The PSUBSCRIBE command is used for matching channel patterns.

PSUBSCRIBE pattern [pattern ...]

This works just the same as a normal SUBSCRIBE command but allows you to match channels with names that match a pattern. This lets publishers be very specific about the information they’re publishing, and allows subscribers to listen to many channels without knowing their precise names.

The supported patterns are simple: * matches any characters, ? matches a single character, and brackets can be used to match a set of acceptable characters, like [acd].

Match temperature readings from several rooms:

PSUBSCRIBE temp-reading:*

Match events from A/B tests like site-link:logo:a:clickrate and site-link:logo:b:clickrate:

PSUBSCRIBE site-link:logo:?:clickrate

Match events across a series of AWS instances, for published events like system-health:us-east-1a:i-36a44b83 and system-health:us-east-1c:i-73657420636f636f6:

PSUBSCRIBE system-health:us-east-1[acd]:i-*

Node.js example

In this example, which could also be used with the earlier publishing example, the temperature is logged along with the room where the temperature was read.

var subscriber = require("redis").createClient();

subscriber.on("pmessage", function(pattern, channel, message) {
  var room = channel.split(":")[1];
  console.log("A temperature of " + message + " was read in " + room);
});

subscriber.psubscribe("temp-reading:*");

The pattern-based subscriber gets a few more details in its callback: not only the channel and message, but the particular pattern that was matched - since subscribers can listen on multiple channels or patterns.

Side Note: Performance Characteristics of PUBLISH

Every command in Redis has a documented time complexity, and most of the time these complexities are intuitive. Since the PUBLISH operation feels very simple (almost like SET) one might assume that its complexity is O(1). In fact the time complexity of PUBLISH grows linearly according to the behavior of subscribers.

When the PUBLISH command runs it must step through all of the patterns subscribed to that might match the channel and all of the subscribers who should receive the message, resulting in a time complexity of O(N+M).

Most deployments will never experience any performance trouble with the complexity of PUBLISH, but tracking the performance of that command over time is still wise – be careful if your code automatically generates patterns of channels to listen on.

Your code here?

We’d love to feature a walk-through of some great examples of apps built using Redis pub/sub. If you know of some good examples, send us a note at support@memetria.com – we’ll do our best to feature some of the code we see.

Last updated 17 Oct 2016. Originally written by Brian P O'Rourke

← Back to docs