Skip to content

第12章:Redis 发布与订阅(消息通知基础)

12.1 发布与订阅的意义

Redis发布与订阅(Pub/Sub)是一种消息通信模式,允许发送者(发布者)向特定频道发送消息,而接收者(订阅者)可以订阅一个或多个频道来接收消息。

核心意义

  • 实现系统间的解耦通信
  • 支持实时消息通知
  • 简化系统架构,减少直接依赖

典型应用场景

  • 系统通知:如登录提醒、操作通知
  • 实时更新:如商品价格变动、库存变化
  • 消息广播:如系统维护公告、活动通知
  • 事件驱动:如用户注册成功后触发后续流程

12.2 核心命令

1. 频道订阅与发布

命令功能示例
SUBSCRIBE订阅一个或多个频道SUBSCRIBE channel1 channel2
PUBLISH向频道发布消息PUBLISH channel1 "Hello Redis"
UNSUBSCRIBE取消订阅一个或多个频道UNSUBSCRIBE channel1
PSUBSCRIBE订阅匹配模式的频道PSUBSCRIBE news:*
PUNSUBSCRIBE取消订阅匹配模式的频道PUNSUBSCRIBE news:*

2. 命令使用示例

示例1:基本的发布与订阅

订阅者端

bash
# 连接Redis并订阅频道
redis-cli

# 订阅名为 "notice" 的频道
SUBSCRIBE notice

# 等待接收消息...
# 当有消息发布时,会显示如下格式:
# 1) "message"
# 2) "notice"
# 3) "Hello from publisher!"

发布者端

bash
# 连接Redis并发布消息
redis-cli

# 向 "notice" 频道发布消息
PUBLISH notice "Hello from publisher!"

# 返回值表示有多少个订阅者接收到了消息
# (integer) 1

示例2:使用模式匹配订阅多个频道

订阅者端

bash
# 订阅所有以 "news:" 开头的频道
PSUBSCRIBE news:*

发布者端

bash
# 向不同的新闻频道发布消息
PUBLISH news:sports "Sports news update"
PUBLISH news:tech "Technology news update"
PUBLISH news:finance "Financial news update"

12.3 发布与订阅的工作原理

  1. 订阅过程

    • 订阅者向Redis服务器发送SUBSCRIBE命令
    • Redis服务器将订阅者与频道关联起来
    • 订阅者进入阻塞状态,等待接收消息
  2. 发布过程

    • 发布者向Redis服务器发送PUBLISH命令
    • Redis服务器查找订阅该频道的所有订阅者
    • Redis服务器将消息发送给所有匹配的订阅者
  3. 消息传递

    • 消息是实时传递的,不存储
    • 订阅者必须在线才能接收消息
    • 消息一旦发送,无法回放或重发

12.4 应用场景实操

场景1:系统消息通知

需求:实现一个简单的系统消息通知功能,当管理员发布通知时,所有在线用户都能收到。

实现步骤

  1. 用户端订阅通知频道

    javascript
    // 前端JavaScript代码
    const redis = require('redis');
    const subscriber = redis.createClient();
    
    subscriber.subscribe('system:notice');
    
    subscriber.on('message', (channel, message) => {
      console.log(`收到系统通知: ${message}`);
      // 显示通知到界面
    });
  2. 管理员发布通知

    bash
    # 管理员执行命令发布通知
    redis-cli
    PUBLISH system:notice "系统将于今晚23:00进行维护,预计持续30分钟"

场景2:实时价格更新

需求:当商品价格变动时,实时通知前端更新显示。

实现步骤

  1. 前端订阅价格频道

    javascript
    // 前端JavaScript代码
    const subscriber = redis.createClient();
    
    // 订阅特定商品的价格频道
    subscriber.subscribe('price:product:1001');
    
    subscriber.on('message', (channel, message) => {
      const priceData = JSON.parse(message);
      console.log(`商品${priceData.productId}价格更新为: ¥${priceData.price}`);
      // 更新界面价格显示
    });
  2. 后端发布价格更新

    javascript
    // 后端Node.js代码
    const publisher = redis.createClient();
    
    // 当价格变动时发布消息
    function updatePrice(productId, newPrice) {
      const priceData = {
        productId: productId,
        price: newPrice,
        timestamp: new Date().toISOString()
      };
      
      publisher.publish(`price:product:${productId}`, JSON.stringify(priceData));
    }
    
    // 调用示例
    updatePrice(1001, 99.99);

12.5 Redis Pub/Sub与其他消息队列的对比

特性Redis Pub/SubRabbitMQKafka
消息存储不存储消息,实时传递支持消息持久化支持消息持久化
消息可靠性低(订阅者离线则丢失)高(支持ACK机制)高(支持偏移量)
消息顺序保证频道内消息顺序保证队列内消息顺序保证分区内消息顺序
扩展性有限良好优秀
复杂度低(易于使用)中(配置复杂)高(学习曲线陡)
适用场景实时通知、简单消息复杂业务场景高吞吐量、大数据

12.6 注意事项与最佳实践

注意事项

  • 消息不持久:Redis Pub/Sub不存储消息,订阅者离线期间的消息会丢失
  • 无消息确认:发布者无法知道消息是否被接收
  • 阻塞操作:订阅后客户端会进入阻塞状态,无法执行其他命令
  • 性能限制:高并发场景下可能成为瓶颈

最佳实践

  • 合理设计频道名称:使用命名空间和层次结构,如 system:noticeprice:product:1001
  • 消息格式标准化:使用JSON格式,包含必要的元数据
  • 避免消息过大:消息大小应控制在合理范围内,避免影响性能
  • 考虑使用专业消息队列:对于关键业务场景,考虑使用RabbitMQ或Kafka

新手易错点

  • 订阅后无法执行其他命令:订阅操作会阻塞客户端,需要在单独的连接中进行
  • 消息发布后未订阅无法接收:Pub/Sub是实时的,未订阅的客户端无法接收历史消息
  • 频道名称拼写错误:发布和订阅的频道名称必须完全匹配
  • 忘记取消订阅:长期运行的应用应在不需要时取消订阅,避免资源泄漏
  • 过度依赖Pub/Sub:对于需要可靠传递的消息,应考虑使用专业的消息队列系统

© 2026 编程马·菜鸟教程 版权所有