Python Redis Pub/Sub 全面教程:常用 API 串联与实战指南

8 阅读7分钟

大家好,我是jobleap.cn的小九。

你想了解 Python 操作 Redis 的 Pub/Sub(发布/订阅)功能的使用方法、应用场景和解决的核心问题,这份教程会从核心概念、环境准备、API 详解到实战案例,全方位讲解 Redis Pub/Sub 在 Python 中的落地使用。

一、Redis Pub/Sub 核心概念与价值

1. 什么是 Redis Pub/Sub

Redis Pub/Sub 是 Redis 提供的发布/订阅消息通信模式

  • 发布者(Publisher):向指定的「频道(Channel)」发送消息,无需关心谁在接收。
  • 订阅者(Subscriber):订阅一个或多个频道,持续监听频道内的消息,无需关心谁在发送。
  • 核心特点:松耦合、实时性、一对多(一个发布者可向多个订阅者推送消息)。

2. 解决的核心问题

  • 系统解耦:避免模块间直接调用,比如订单模块无需直接调用通知模块,只需向「订单完成」频道发消息,通知模块订阅该频道即可。
  • 实时通信:实现服务间/客户端间的实时消息推送(如实时通知、聊天、监控告警)。
  • 广播通知:一次发布,多个订阅者同时接收(如系统公告、配置更新推送)。
  • 流量削峰:非核心流程通过 Pub/Sub 异步处理,避免主流程阻塞。

3. 典型应用场景

  • 实时消息通知(如电商订单支付成功通知、用户注册欢迎语);
  • 系统监控告警(如服务器CPU过高时推送告警消息);
  • 微服务间异步通信;
  • 聊天室、实时弹幕等互动场景;
  • 配置中心的配置更新广播。

二、环境准备

1. 安装依赖

Python 操作 Redis 需安装 redis 库(推荐 4.x+ 版本),执行以下命令:

pip install redis

2. 确保 Redis 服务运行

  • 本地 Redis:启动 Redis 服务(默认端口 6379,无密码);
  • 远程 Redis:记录地址、端口、密码,后续连接时配置。

三、Python Redis Pub/Sub 常用 API 详解

1. 基础连接配置

首先初始化 Redis 连接,这是 Pub/Sub 的基础:

import redis

# 初始化 Redis 客户端(根据实际情况修改参数)
redis_client = redis.Redis(
    host="localhost",  # Redis 地址
    port=6379,         # Redis 端口
    password="",       # Redis 密码(无则留空)
    db=0,              # 使用的数据库编号
    decode_responses=True  # 自动将返回的字节串转为字符串(避免手动解码)
)

2. 发布者(Publisher)核心 API

发布者的核心操作是 publish(),向指定频道发送消息:

def publish_message(channel: str, message: str):
    """
    向指定频道发布消息
    :param channel: 频道名称
    :param message: 要发送的消息
    :return: 接收消息的订阅者数量
    """
    # 核心 API:publish(频道名, 消息内容)
    subscriber_count = redis_client.publish(channel, message)
    print(f"向频道 {channel} 发布消息:{message}")
    print(f"本次有 {subscriber_count} 个订阅者接收消息")
    return subscriber_count

# 调用示例
if __name__ == "__main__":
    publish_message("order_notify", "订单【123456】支付成功!")

关键说明

  • publish(channel, message) 返回值是「当前订阅该频道的在线订阅者数量」;
  • 消息内容可以是字符串、JSON 字符串(复杂数据推荐),示例中用简单字符串演示。

3. 订阅者(Subscriber)核心 API

订阅者需要创建「订阅对象」,并持续监听消息,核心 API 包括 pubsub()subscribe()listen()

def subscribe_channel(channel: str):
    """
    订阅指定频道,持续监听消息
    :param channel: 频道名称
    """
    # 1. 创建 Pub/Sub 对象
    pubsub = redis_client.pubsub()
    
    # 2. 订阅指定频道(可订阅多个,如 subscribe(chan1, chan2))
    pubsub.subscribe(channel)
    print(f"已订阅频道 {channel},等待消息...\n")
    
    # 3. 持续监听消息(阻塞式)
    for message in pubsub.listen():
        """
        message 是字典格式,核心字段:
        - type: 消息类型(subscribe/psubscribe 表示订阅成功;message/pmessage 表示收到消息)
        - channel: 消息所属频道
        - data: 消息内容(type=message 时有效)
        """
        if message["type"] == "message":
            # 仅处理实际的消息(过滤订阅成功的通知)
            print(f"收到频道 {message['channel']} 的消息:{message['data']}")

# 调用示例
if __name__ == "__main__":
    subscribe_channel("order_notify")

关键说明

  • pubsub():创建 Pub/Sub 操作对象,所有订阅/取消订阅都基于该对象;
  • subscribe(channel1, channel2...):订阅一个或多个固定频道;
  • listen():阻塞式监听消息,会一直运行直到手动停止(如 Ctrl+C);
  • 消息字典的 type 字段需过滤:只有 message 类型是实际的业务消息,subscribe 只是订阅成功的通知。

4. 进阶 API:模式订阅(Pattern Subscribe)

如果需要订阅「一类频道」(如所有以 notify_ 开头的频道),可使用 psubscribe()

def pattern_subscribe(pattern: str):
    """
    模式订阅(匹配一类频道)
    :param pattern: 匹配模式(如 notify_* 表示所有以 notify_ 开头的频道)
    """
    pubsub = redis_client.pubsub()
    # 模式订阅:匹配所有以 notify_ 开头的频道
    pubsub.psubscribe(pattern)
    print(f"已模式订阅 {pattern},等待消息...\n")
    
    for message in pubsub.listen():
        if message["type"] == "pmessage":
            # 模式订阅的消息 type 是 pmessage
            print(f"收到匹配频道 {message['channel']} 的消息:{message['data']}")

# 调用示例:订阅所有 notify_ 开头的频道
pattern_subscribe("notify_*")

5. 取消订阅

可通过 unsubscribe()/punsubscribe() 取消指定频道的订阅:

def unsubscribe_demo():
    pubsub = redis_client.pubsub()
    pubsub.subscribe("order_notify", "user_notify")
    
    # 取消订阅单个频道
    pubsub.unsubscribe("order_notify")
    # 取消订阅所有频道
    # pubsub.unsubscribe()
    
    # 模式订阅的取消
    # pubsub.punsubscribe("notify_*")

四、实战案例:实时订单通知系统

1. 需求场景

电商系统中,订单支付成功后,发布者向 order_notify 频道推送订单消息,两个订阅者分别处理:

  • 订阅者1:发送短信通知用户;
  • 订阅者2:记录订单日志到本地。

2. 完整代码实现

(1)发布者:订单支付成功后发布消息

# publisher.py
import redis
import json

# 初始化 Redis 客户端
redis_client = redis.Redis(
    host="localhost",
    port=6379,
    password="",
    decode_responses=True
)

def publish_order_success(order_id: str, user_phone: str, amount: float):
    """发布订单支付成功消息(用 JSON 封装复杂数据)"""
    # 构造复杂消息体(推荐用 JSON 字符串)
    message = json.dumps({
        "order_id": order_id,
        "user_phone": user_phone,
        "amount": amount,
        "timestamp": "2026-01-27 10:00:00"
    })
    # 发布到 order_notify 频道
    redis_client.publish("order_notify", message)
    print(f"订单 {order_id} 支付成功,已发布通知消息")

# 模拟订单支付成功
if __name__ == "__main__":
    publish_order_success("OD20260127001", "13800138000", 99.0)

(2)订阅者1:短信通知

# subscriber_sms.py
import redis
import json

redis_client = redis.Redis(
    host="localhost",
    port=6379,
    password="",
    decode_responses=True
)

def send_sms_notify():
    """订阅订单频道,发送短信通知"""
    pubsub = redis_client.pubsub()
    pubsub.subscribe("order_notify")
    print("短信通知服务已启动,等待订单消息...")
    
    for message in pubsub.listen():
        if message["type"] == "message":
            # 解析 JSON 消息
            order_data = json.loads(message["data"])
            # 模拟发送短信(实际场景调用短信API)
            print(f"【短信通知】用户 {order_data['user_phone']},您的订单 {order_data['order_id']} 支付成功,金额 {order_data['amount']} 元")

if __name__ == "__main__":
    send_sms_notify()

(3)订阅者2:订单日志

# subscriber_log.py
import redis
import json
from datetime import datetime

redis_client = redis.Redis(
    host="localhost",
    port=6379,
    password="",
    decode_responses=True
)

def record_order_log():
    """订阅订单频道,记录订单日志"""
    pubsub = redis_client.pubsub()
    pubsub.subscribe("order_notify")
    print("订单日志服务已启动,等待订单消息...")
    
    for message in pubsub.listen():
        if message["type"] == "message":
            order_data = json.loads(message["data"])
            # 模拟记录日志(实际场景可写入文件/数据库)
            log_content = f"{datetime.now()} - 订单 {order_data['order_id']} 支付成功,金额 {order_data['amount']} 元,用户手机号 {order_data['user_phone']}"
            with open("order_log.txt", "a", encoding="utf-8") as f:
                f.write(log_content + "\n")
            print(f"【日志记录】{log_content}")

if __name__ == "__main__":
    record_order_log()

3. 运行与测试

  1. 启动 Redis 服务;
  2. 分别打开 3 个终端,依次运行:
    • 终端1:python subscriber_sms.py(启动短信订阅者);
    • 终端2:python subscriber_log.py(启动日志订阅者);
    • 终端3:python publisher.py(发布订单消息);
  3. 观察终端1和终端2的输出,会实时收到订单消息并处理。

五、注意事项与避坑指南

  1. 消息不持久化:Redis Pub/Sub 是「即发即失」的,订阅者离线期间的消息会丢失,若需持久化消息,需使用 Redis Stream 或 Kafka;
  2. 阻塞监听listen() 是阻塞式的,若需在主线程不阻塞,可结合多线程/多进程使用;
  3. 连接稳定性:若 Redis 连接断开,订阅者需重新连接并订阅;
  4. 消息大小:Redis 单条消息不宜过大(建议 < 1MB),避免占用过多内存;
  5. 性能限制:Redis Pub/Sub 适合轻量级实时通信,高并发场景建议用专业消息队列(如 Kafka、RabbitMQ)。

总结

  1. 核心价值:Redis Pub/Sub 解决了系统解耦、实时通信、广播通知的问题,是轻量级的消息通信方案;
  2. 核心 API:发布用 publish(channel, message),订阅用 pubsub().subscribe(channel) + listen(),模式订阅用 psubscribe()
  3. 关键注意:消息不持久化、监听是阻塞式的,高可靠/高并发场景需结合其他组件(如 Stream、Kafka)。

这份教程覆盖了 Python Redis Pub/Sub 的核心用法和实战场景,你可以基于此扩展到自己的业务场景中,比如替换为实际的短信API、日志存储方案等。