Of course! Here's a comprehensive guide to using Python with Redis Pub/Sub, including a complete, runnable example and explanations of key concepts.

What is Redis Pub/Sub?
Redis Pub/Sub (Publish/Subscribe) is a messaging paradigm where publishers send messages to channels, and subscribers listen to specific channels and receive messages published to them.
Key Characteristics:
- Decoupling: Publishers and subscribers are completely decoupled. A publisher doesn't need to know anything about its subscribers (who they are, how many there are, or even if they exist). This allows for highly scalable and flexible systems.
- Broadcast: A message published to a channel is delivered to all active subscribers of that channel.
- No Persistence: Messages are delivered in real-time to connected subscribers. If a subscriber is not connected when a message is published, the message is lost. This is not a queuing system like Redis Lists or Streams.
- Fire-and-Forget: The publisher's job is done as soon as the message is sent to the channel. It doesn't wait for an acknowledgment from subscribers.
Setup: Prerequisites
First, you need to have a Redis server running. If you don't have one, the easiest way is to use Docker:
docker run -d -p 6379:6379 redis
This command runs a Redis container in the background and maps its default port (6379) to your local machine.

Next, install the Python Redis client library:
pip install redis
The Core Components
redis.Redis(): The main client object for connecting to Redis.pubsub = redis_client.pubsub(): This creates aPubSubobject. This object is your primary interface for subscribing to channels and listening for messages.pubsub.subscribe('channel_name'): Subscribes the client to one or more channels.- *`pubsub.psubscribe('pattern')
**: Subscribes to channels matching a pattern (e.g.,news:*`). We'll cover this later. publish('channel_name', 'message'): The method on the mainredis.Redisclient to publish a message to a channel.pubsub.listen(): A generator that blocks and yields messages as they are received from the subscribed channels.pubsub.get_message(): A non-blocking method that can be used in a loop to check for new messages.
A Complete, Runnable Example
Let's create a publisher and a subscriber in separate Python scripts to see them interact.
publisher.py
This script will publish messages to a channel named notifications.
import redis
import time
import random
# Connect to Redis
r = redis.Redis(host='localhost', port=6379, db=0)
channel_name = 'notifications'
messages = [
"New user registered!",
"Server backup completed.",
"Payment received for order #12345.",
"System maintenance scheduled for tonight."
]
print(f"Publisher: Sending messages to channel '{channel_name}'...")
try:
for i in range(5):
message = random.choice(messages)
# Publish the message
r.publish(channel_name, message)
print(f"Published: {message}")
time.sleep(2) # Wait 2 seconds before sending the next message
except KeyboardInterrupt:
print("Publisher shutting down.")
subscriber.py
This script will subscribe to the notifications channel and print any messages it receives.

import redis
import time
# Connect to Redis
r = redis.Redis(host='localhost', port=6379, db=0)
channel_name = 'notifications'
# Create a PubSub object and subscribe to the channel
pubsub = r.pubsub()
pubsub.subscribe(channel_name)
print(f"Subscriber: Subscribed to channel '{channel_name}'. Waiting for messages...")
print("Press Ctrl+C to exit.")
try:
# The listen() method blocks and yields messages as they arrive
for message in pubsub.listen():
# The message object is a dictionary. We're interested in the 'data' field.
# The first message after subscribing is a confirmation message.
if message['type'] == 'subscribe':
print(f"Subscribed successfully to {message['channel']}.")
continue
# The actual message data is in bytes, so we decode it to a string.
if message['type'] == 'message':
received_channel = message['channel'].decode('utf-8')
received_message = message['data'].decode('utf-8')
print(f"Received on channel '{received_channel}': {received_message}")
except KeyboardInterrupt:
print("\nUnsubscribing and shutting down.")
pubsub.unsubscribe(channel_name)
finally:
# Always close the connection to prevent resource leaks
pubsub.close()
How to Run It
-
Open two separate terminal windows.
-
In the first terminal, run the subscriber:
python subscriber.py
You will see:
Subscriber: Subscribed to channel 'notifications'. Waiting for messages... -
In the second terminal, run the publisher:
python publisher.py
The publisher will start sending messages every 2 seconds.
-
Switch back to the first terminal (subscriber). You will see the messages appear in real-time as they are published.
Understanding the Message Format
When you call pubsub.listen(), each yielded message is a dictionary. The most common keys are:
type: The type of the message.'subscribe': Sent when you successfully subscribe to a channel.'unsubscribe': Sent when you unsubscribe.'message': An actual message published to a channel you are subscribed to.- `'pmessage'**: A message from a pattern subscription (see below).
channel: The name of the channel the message was sent to (as bytes).pattern: The pattern that matched the channel (only for'pmessage').data: The content of the message (as bytes).
Important: Both channel and data are returned as byte strings. You must decode them to work with them as regular Python strings (e.g., message['channel'].decode('utf-8')).
Pattern Subscriptions (psubscribe)
Sometimes you want to subscribe to multiple channels that follow a naming convention (e.g., news:us, news:uk, news:fr). Instead of subscribing to each one individually, you can use pattern subscriptions.
The psubscribe method lets you subscribe to all channels matching a given pattern, which can include wildcards:
- matches any single "word" (separated by dots or slashes).
- matches zero or more words.
- matches any single character.
[]matches a single character in the set.
pattern_subscriber.py
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
# Subscribe to all channels starting with 'news:'
pubsub = r.pubsub()
pubsub.psubscribe('news:*')
print("Pattern Subscriber: Subscribed to all channels matching 'news:*'")
try:
for message in pubsub.listen():
if message['type'] == 'psubscribe':
print(f"Subscribed to pattern {message['pattern']}")
continue
if message['type'] == 'pmessage':
# For pmessage, the channel name is in the 'channel' key
# and the pattern that matched is in the 'pattern' key
received_channel = message['channel'].decode('utf-8')
received_pattern = message['pattern'].decode('utf-8')
received_message = message['data'].decode('utf-8')
print(f"Pattern '{received_pattern}' matched channel '{received_channel}': {received_message}")
except KeyboardInterrupt:
print("\nUnsubscribing and shutting down.")
pubsub.punsubscribe('news:*')
finally:
pubsub.close()
You can run this alongside a publisher that sends messages to news:sports and news:politics, and the pattern subscriber will receive both.
Unsubscribing and Closing Connections
It's crucial to manage your subscriptions and connections properly to avoid resource leaks on both the client and the Redis server.
pubsub.unsubscribe('channel_name'): Unsubscribes from a specific channel.pubsub.punsubscribe('pattern'): Unsubscribes from a pattern.pubsub.unsubscribe(): Unsubscribes from all channels.pubsub.close(): Closes the connection to the Redis server for thisPubSubinstance. This is the most important cleanup step.
When to Use Pub/Sub vs. Other Redis Data Types
| Feature | Pub/Sub | Lists (as a Queue) | Streams |
|---|---|---|---|
| Use Case | Real-time notifications, event broadcasting, decoupled services. | Task queues, message passing between services. | Advanced data structures, event sourcing, reliable logs. |
| Message Delivery | Fire-and-forget. No persistence. | Reliable. Messages persist until a consumer reads and acknowledges them. | Persistent & Reliable. Messages are stored and can be read by multiple consumers. |
| Consumer Groups | No. | No. | Yes. Allows multiple consumers to work on a stream in parallel. |
| Consumer Acknowledgment | No. | Yes (via RPOPLPUSH or XREADGROUP with XACK). |
Yes (XACK). |
| Scalability | Excellent for fan-out (one-to-many). | Good for one-to-one or one-to-few. | Excellent for high-throughput, many-to-many scenarios. |
In short:
- Use Pub/Sub for real-time notifications where losing a message if a subscriber is down is acceptable.
- Use Lists as a simple, reliable queue for background jobs.
- Use Streams for complex, stateful, and reliable event processing.
