上一篇我们深入学习了Redis SETNX分布式锁,依靠原子排他性解决了高并发下的缓存击穿、数据错乱等互斥问题,筑牢了分布式场景的数据安全防线。而在日常开发中,我们还常常需要实现实时消息推送、系统通知、状态同步等功能,比如博客评论通知、站内消息推送、配置实时更新。

针对这类轻量级实时通信场景,Redis自带的发布订阅(Pub/Sub)模式无需引入额外中间件,开箱即用,是实现简单实时通知的首选方案。本篇从核心原理、命令实操、代码落地、场景实战到避坑要点,带你快速掌握Redis Pub/Sub,轻松搭建轻量级实时通知系统。

核心定位:Redis Pub/Sub是一种消息通信模式,基于“发布者-频道-订阅者”架构,发布者向指定频道推送消息,所有订阅该频道的订阅者均可实时接收,实现一对多的消息广播与实时通知。

一、Redis Pub/Sub 核心原理与角色

发布订阅模式彻底解耦发布者和订阅者,双方无需直接交互,依托频道(Channel)实现消息转发,核心角色清晰易懂:

  • 发布者(Publisher):消息的发送方,负责向指定频道推送消息,无需关心谁订阅了该频道
  • 订阅者(Subscriber):消息的接收方,订阅指定频道后,可实时接收该频道的所有消息
  • 频道(Channel):消息的中转载体,相当于消息队列的主题,支持自定义命名

核心通信流程

  1. 订阅者提前订阅指定频道,持续监听消息
  2. 发布者向指定频道发布消息
  3. Redis服务器将消息推送给所有订阅该频道的订阅者
  4. 订阅者实时接收并处理消息

关键特性:Pub/Sub模式是 fire and forget(发完即弃),消息不持久化、不堆积;订阅者离线期间的消息会丢失,适合非核心、可丢失的轻量级通知场景。


二、Redis Pub/Sub 核心命令实操

Redis Pub/Sub命令简洁直观,分为订阅、发布、取消订阅三大类,可直接通过Redis客户端执行测试。

命令作用示例
SUBSCRIBE channel1 [channel2...]订阅一个或多个频道SUBSCRIBE blog:notice user:msg
PUBLISH channel message向指定频道发布消息PUBLISH blog:notice "您的博客有新评论"
UNSUBSCRIBE [channel...]取消订阅指定频道,无参数则取消全部UNSUBSCRIBE blog:notice
PSUBSCRIBE pattern1 [pattern2...]按通配符模式订阅频道PSUBSCRIBE blog:*
PUNSUBSCRIBE [pattern...]取消通配符模式订阅PUNSUBSCRIBE blog:*

命令测试小案例

  1. 打开客户端1,执行订阅:SUBSCRIBE blog:comment
  2. 打开客户端2,发布消息:PUBLISH blog:comment "文章《Redis入门》有新评论啦"
  3. 客户端1会实时收到该消息,完成一次简单的实时通知

三、实战:SpringBoot实现Pub/Sub实时通知

基于SpringBoot整合Redis,实现博客评论实时通知场景,发布者发布评论消息,订阅者实时接收并推送至前端,代码可直接复用。

第一步:定义频道规范

沿用Redis命名规范,频道名见名知意,便于维护:

  • 博客评论通知:blog:comment:notice
  • 用户站内消息:user:message:notice
  • 系统公告推送:system:announcement

第二步:配置Redis消息监听

通过RedisMessageListenerContainer配置消息监听容器,绑定订阅频道和消息处理器。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import javax.annotation.Resource;

/**
 * Redis Pub/Sub 配置类
 */
@Configuration
public class RedisPubSubConfig {

    // 评论通知频道
    public static final String COMMENT_CHANNEL = "blog:comment:notice";

    @Resource
    private RedisMessageListener redisMessageListener;

    /**
     * 消息监听容器:绑定订阅频道和处理器
     */
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        // 订阅评论通知频道
        container.addMessageListener(messageListenerAdapter(), new ChannelTopic(COMMENT_CHANNEL));
        return container;
    }

    /**
     * 消息监听器适配器
     */
    @Bean
    public MessageListenerAdapter messageListenerAdapter() {
        return new MessageListenerAdapter(redisMessageListener, "onMessage");
    }

    /**
     * RedisTemplate配置(用于发布消息)
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        // 序列化配置(省略通用序列化代码)
        return redisTemplate;
    }
}

第三步:自定义消息监听器

实现消息接收逻辑,订阅者收到消息后进行业务处理(如推送前端、记录日志)。

import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;

/**
 * Redis 消息订阅监听器
 */
@Component
public class RedisMessageListener implements MessageListener {

    @Override
    public void onMessage(Message message, byte[] pattern) {
        // 解析频道和消息内容
        String channel = new String(message.getChannel());
        String msgContent = new String(message.getBody());
        // 业务处理:实时推送前端、打印通知、更新状态等
        System.out.println("【实时通知】收到频道[" + channel + "]的消息:" + msgContent);
        // 可对接WebSocket推送给前端用户
    }
}

第四步:消息发布者实现

业务触发时(如新增评论),调用发布接口推送消息至指定频道。

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import static com.example.config.RedisPubSubConfig.COMMENT_CHANNEL;

/**
 * 消息发布服务
 */
@Service
public class RedisPublishService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 发布博客评论通知
     * @param articleId 文章ID
     * @param commentContent 评论内容
     */
    public void publishCommentNotice(Long articleId, String commentContent) {
        String message = String.format("文章[%d]收到新评论:%s", articleId, commentContent);
        // 向频道发布消息
        redisTemplate.convertAndSend(COMMENT_CHANNEL, message);
        System.out.println("发布评论通知成功:" + message);
    }
}

第五步:业务调用测试

@RestController
public class CommentController {

    @Resource
    private RedisPublishService redisPublishService;

    /**
     * 新增评论,同时发布实时通知
     */
    @PostMapping("/comment/add")
    public String addComment(Long articleId, String content) {
        // 1. 保存评论至数据库(省略业务代码)
        // 2. 发布实时通知
        redisPublishService.publishCommentNotice(articleId, content);
        return "评论发布成功,通知已推送";
    }
}

四、Redis Pub/Sub 适用场景与局限性

✅ 适合的场景

  • 轻量级实时通知:博客评论、点赞、站内消息、系统公告
  • 配置实时更新:多节点服务配置同步、开关状态推送
  • 简单广播消息:日志同步、状态广播、非核心消息推送
  • 无持久化需求:允许消息丢失、不要求重试的场景

❌ 不适合的场景

  • 核心业务消息:订单通知、支付结果、验证码(消息丢失会影响业务)
  • 持久化需求:需要消息留存、离线重试的场景
  • 高可靠性场景:金融、交易类消息通信
  • 消息堆积:大量消息积压会占用Redis内存

五、Pub/Sub 避坑指南与最佳实践

1. 牢记消息不持久化

Redis重启、订阅者离线期间的消息会彻底丢失,核心消息严禁使用Pub/Sub,建议改用Redis Stream、RabbitMQ等可靠消息队列。

2. 控制消息体积与频率

避免发送超大体积消息,高频广播消息会占用Redis带宽和内存,影响Redis核心性能。

3. 合理设计频道名

采用层级命名规范(如业务:模块:用途),避免频道名冲突,支持通配符批量订阅。

4. 避免订阅者阻塞

消息处理逻辑要轻量化,耗时业务需异步处理,防止阻塞消息监听,导致后续消息延迟接收。

5. 替代方案选型

需要消息持久化、可靠性投递时,优先选用Redis Stream(Redis原生消息队列)或专业MQ(RabbitMQ、RocketMQ)。


六、核心总结

核心结论:Redis Pub/Sub是轻量级实时通知的极简方案,实现简单、性能高效、零额外依赖,通过“发布-频道-订阅”模型轻松实现一对多消息广播。适合非核心、可丢失的实时通知场景,是中小型项目实现实时推送的首选。

结语与下篇预告

本篇我们掌握了Redis Pub/Sub模式的原理与实战,实现了轻量级实时通知功能,填补了Redis消息通信场景的空白。

发表回复