Redis Pub/Sub es un sistema de mensajería publicar/suscribir integrado en Redis: los publicadores envían mensajes a un canal y Redis los entrega al instante a todos los clientes suscritos en ese momento. Es ideal para notificaciones en vivo, invalidación de cachés o actualizaciones de paneles cuando ya usas Redis. En esta guía instalarás Redis en Ubuntu 24.04, probarás Pub/Sub con redis-cli, escribirás un publicador y un suscriptor en Python y verás sus límites frente a Redis Streams.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. - Python 3 (incluido en Ubuntu 24.04).
- Dos terminales abiertas en el servidor para ver publicador y suscriptor a la vez.
Paso 1: Instalar Redis
Ubuntu 24.04 incluye Redis 7 en sus repositorios oficiales, suficiente para todo lo que se ve en esta guía:
sudo apt update
sudo apt install redis-server
El paquete arranca y habilita el servicio automáticamente. Compruébalo:
systemctl status redis-server --no-pager
● redis-server.service - Advanced key-value store
Loaded: loaded (/usr/lib/systemd/system/redis-server.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-09-24 10:12:03 UTC; 8s ago
Y que responde:
redis-cli ping
PONG
Paso 2: Proteger Redis con contraseña
Por defecto Redis en Ubuntu solo escucha en 127.0.0.1 y ::1, así que no es accesible desde fuera. Aun así, conviene exigir contraseña para que cualquier proceso local no pueda leer ni publicar mensajes. Abre la configuración:
sudo nano /etc/redis/redis.conf
Busca la línea # requirepass foobared, descoméntala y pon una contraseña robusta en lugar de your_strong_password. Comprueba también que bind sigue limitado a localhost:
bind 127.0.0.1 -::1
requirepass your_strong_password
Reinicia Redis para aplicar el cambio:
sudo systemctl restart redis-server
Para no escribir la contraseña en cada comando (ni dejarla en el historial con -a), redis-cli lee la variable de entorno REDISCLI_AUTH. Expórtala en cada terminal que uses:
read -rs REDISCLI_AUTH && export REDISCLI_AUTH
Escribe la contraseña y pulsa Enter (no se muestra en pantalla). Verifica la autenticación:
redis-cli ping
PONG
Si ves NOAUTH Authentication required., la variable no está definida en esa terminal.
Paso 3: Publicar y suscribirse con redis-cli
Un canal de Pub/Sub no se crea ni se declara: existe mientras alguien está suscrito. En la primera terminal, suscríbete al canal notificaciones:
redis-cli SUBSCRIBE notificaciones
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "notificaciones"
3) (integer) 1
La conexión queda bloqueada esperando mensajes. En la segunda terminal (con REDISCLI_AUTH exportada), publica un mensaje:
redis-cli PUBLISH notificaciones "Mantenimiento programado a las 02:00"
(integer) 1
El número que devuelve PUBLISH es cuántos clientes recibieron el mensaje. En la primera terminal aparece al momento:
1) "message"
2) "notificaciones"
3) "Mantenimiento programado a las 02:00"
Ahora detén el suscriptor con Ctrl+C y vuelve a publicar:
redis-cli PUBLISH notificaciones "Nadie escucha"
(integer) 0
Ese mensaje se ha perdido: Redis no guarda nada para suscriptores futuros. Esta es la característica más importante de Pub/Sub y la razón para elegir Streams en ciertos casos (Paso 6).
Paso 4: Suscribirse a patrones con PSUBSCRIBE
Con PSUBSCRIBE un cliente recibe los mensajes de todos los canales que coinciden con un patrón de tipo glob (*, ?, [abc]). Es útil cuando el nombre del canal incluye un identificador, como usuario:<id>:eventos. En la primera terminal:
redis-cli PSUBSCRIBE 'usuario:*:eventos'
En la segunda, publica en dos canales distintos:
redis-cli PUBLISH usuario:42:eventos "login"
redis-cli PUBLISH usuario:77:eventos "cambio de contraseña"
El suscriptor recibe ambos como pmessage, con el patrón y el canal concreto:
1) "pmessage"
2) "usuario:*:eventos"
3) "usuario:42:eventos"
4) "login"
1) "pmessage"
2) "usuario:*:eventos"
3) "usuario:77:eventos"
4) "cambio de contraseña"
Con el suscriptor todavía activo, consulta desde la segunda terminal qué canales y patrones hay en uso con el comando PUBSUB:
redis-cli PUBSUB CHANNELS
redis-cli PUBSUB NUMPAT
(empty array)
(integer) 1
PUBSUB CHANNELS solo lista canales con suscripciones directas (SUBSCRIBE), no los cubiertos por patrones; por eso sale vacío. PUBSUB NUMSUB canal devuelve el número de suscriptores directos de un canal concreto.
NotaEn Redis Cluster,
PUBLISHse reenvía a todos los nodos. Desde Redis 7 existenSPUBLISHySSUBSCRIBE(Pub/Sub fragmentado), que solo envían el mensaje al fragmento propietario del canal y escalan mejor en clústeres grandes. En una instancia única no hay diferencia.
Paso 5: Publicador y suscriptor en Python
En una aplicación real usarás una biblioteca cliente. Ubuntu 24.04 no permite instalar paquetes con pip a nivel de sistema, así que crea un entorno virtual para el proyecto:
sudo apt install python3-venv
mkdir -p ~/redis-pubsub && cd ~/redis-pubsub
python3 -m venv venv
./venv/bin/pip install redis
Los scripts leen la contraseña de la variable REDIS_PASSWORD en lugar de llevarla escrita en el código. Expórtala en ambas terminales:
export REDIS_PASSWORD="$REDISCLI_AUTH"
El suscriptor
Crea suscriptor.py:
nano ~/redis-pubsub/suscriptor.py
import json
import os
import redis
r = redis.Redis(
host="127.0.0.1",
port=6379,
password=os.environ.get("REDIS_PASSWORD"),
decode_responses=True,
)
# ignore_subscribe_messages oculta las confirmaciones de suscripción
pubsub = r.pubsub(ignore_subscribe_messages=True)
pubsub.subscribe("notificaciones")
pubsub.psubscribe("usuario:*:eventos")
print("Esperando mensajes (Ctrl+C para salir)...")
try:
for message in pubsub.listen():
try:
data = json.loads(message["data"])
except (TypeError, json.JSONDecodeError):
data = message["data"]
print(f"[{message['channel']}] {data}")
except KeyboardInterrupt:
pass
finally:
pubsub.close()
listen() es un generador bloqueante: espera en el socket sin consumir CPU y devuelve un diccionario por mensaje con las claves type, pattern, channel y data.
El publicador
Crea publicador.py:
nano ~/redis-pubsub/publicador.py
import json
import os
import sys
from datetime import datetime, timezone
import redis
r = redis.Redis(
host="127.0.0.1",
port=6379,
password=os.environ.get("REDIS_PASSWORD"),
decode_responses=True,
)
def publicar_evento(user_id: int, evento: str) -> int:
canal = f"usuario:{user_id}:eventos"
payload = {
"evento": evento,
"fecha": datetime.now(timezone.utc).isoformat(),
}
return r.publish(canal, json.dumps(payload))
if __name__ == "__main__":
user_id = int(sys.argv[1]) if len(sys.argv) > 1 else 42
receptores = publicar_evento(user_id, "login")
print(f"Mensaje entregado a {receptores} suscriptor(es)")
Probarlo
En la primera terminal, arranca el suscriptor:
cd ~/redis-pubsub
./venv/bin/python suscriptor.py
En la segunda, publica un evento:
cd ~/redis-pubsub
./venv/bin/python publicador.py 42
Mensaje entregado a 1 suscriptor(es)
El suscriptor lo muestra ya decodificado:
Esperando mensajes (Ctrl+C para salir)...
[usuario:42:eventos] {'evento': 'login', 'fecha': '2026-09-24T10:31:07.482113+00:00'}
Si ejecutas el publicador sin el suscriptor en marcha, el resultado será 0 y el mensaje se descarta.
Paso 6: Pub/Sub frente a Redis Streams
Pub/Sub entrega cada mensaje como máximo una vez y solo a los clientes conectados en ese instante. Si tu suscriptor se reinicia o pierde la conexión, pierde los mensajes publicados mientras tanto. Redis Streams, en cambio, guarda los mensajes y permite releerlos o repartirlos entre varios consumidores con confirmación (XACK).
| Característica | Pub/Sub | Streams |
|---|---|---|
| Persistencia | No, se descarta al entregar | Sí, queda en el stream |
| Suscriptor desconectado | Pierde los mensajes | Los lee al volver |
| Confirmación de entrega | No | Sí, con grupos de consumidores |
| Reparto de carga entre workers | No, todos reciben todo | Sí, con XREADGROUP |
| Latencia | Mínima | Muy baja |
| Uso típico | Notificaciones en vivo, invalidar cachés, chat | Colas de trabajo, eventos que no se pueden perder |
Para ver la diferencia, añade una entrada a un stream sin que nadie la esté leyendo:
redis-cli XADD eventos '*' tipo login user_id 42
"1790243467123-0"
Y léela después desde el principio (0):
redis-cli XREAD COUNT 10 STREAMS eventos 0
1) 1) "eventos"
2) 1) 1) "1790243467123-0"
2) 1) "tipo"
2) "login"
3) "user_id"
4) "42"
El mensaje seguía ahí aunque no hubiese consumidores cuando se escribió. Si no puedes permitirte perder eventos, usa Streams con grupos de consumidores (o un broker como RabbitMQ) en lugar de intentar añadir fiabilidad encima de Pub/Sub.
Solución de problemas
El suscriptor se desconecta con mensajes grandes o mucho volumen
Redis acumula en memoria los mensajes pendientes de enviar a cada suscriptor. Si un cliente lee más despacio de lo que se publica, Redis lo desconecta al superar client-output-buffer-limit para la clase pubsub (por defecto 32 MB de límite duro, o 8 MB mantenidos durante 60 segundos). Consulta el valor actual:
redis-cli CONFIG GET client-output-buffer-limit
La solución habitual es que el suscriptor procese más rápido (por ejemplo, delegando el trabajo pesado a otra cola) en lugar de subir el límite sin más.
NOAUTH Authentication required
El cliente no envía contraseña. En redis-cli, exporta REDISCLI_AUTH; en Python, comprueba que REDIS_PASSWORD está definida en la terminal donde ejecutas el script.
El suscriptor no recibe nada
Comprueba que publicador y suscriptor usan exactamente el mismo nombre de canal (distingue mayúsculas) y que el patrón de PSUBSCRIBE va entre comillas simples en la shell, para que Bash no expanda el *. Mientras el suscriptor está activo, redis-cli PUBSUB NUMSUB notificaciones debe devolver al menos 1.
Conclusión
Has instalado y protegido Redis en Ubuntu 24.04, probado SUBSCRIBE, PSUBSCRIBE y PUBLISH desde redis-cli, y construido un publicador y un suscriptor en Python. Pub/Sub es la opción más sencilla y rápida para mensajes efímeros en tiempo real; para eventos que no pueden perderse, Redis Streams es la herramienta adecuada. Como siguientes pasos, puedes ejecutar el suscriptor como servicio de systemd, conectar Pub/Sub a un servidor WebSocket para enviar notificaciones al navegador, o explorar los grupos de consumidores de Redis Streams.
