上一篇我们深入学习了Redis SETNX分布式锁,依靠原子排他性解决了高并发下的缓存击穿、数据错乱等互斥问题,筑牢了分布式场景的数据安全防线。而在日常开发中,我们还常常需要实现实时消息推送、系统通知、状态同步等功能,比如博客评论通知、站内消息推送、配置实时更新。
针对这类轻量级实时通信场景,Redis自带的发布订阅(Pub/Sub)模式无需引入额外中间件,开箱即用,是实现简单实时通知的首选方案。本篇从核心原理、命令实操、代码落地、场景实战到避坑要点,带你快速掌握Redis Pub/Sub,轻松搭建轻量级实时通知系统。
核心定位:Redis Pub/Sub是一种消息通信模式,基于“发布者-频道-订阅者”架构,发布者向指定频道推送消息,所有订阅该频道的订阅者均可实时接收,实现一对多的消息广播与实时通知。
一、Redis Pub/Sub 核心原理与角色
发布订阅模式彻底解耦发布者和订阅者,双方无需直接交互,依托频道(Channel)实现消息转发,核心角色清晰易懂:
- 发布者(Publisher):消息的发送方,负责向指定频道推送消息,无需关心谁订阅了该频道
- 订阅者(Subscriber):消息的接收方,订阅指定频道后,可实时接收该频道的所有消息
- 频道(Channel):消息的中转载体,相当于消息队列的主题,支持自定义命名
核心通信流程
- 订阅者提前订阅指定频道,持续监听消息
- 发布者向指定频道发布消息
- Redis服务器将消息推送给所有订阅该频道的订阅者
- 订阅者实时接收并处理消息
关键特性: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,执行订阅:
SUBSCRIBE blog:comment - 打开客户端2,发布消息:
PUBLISH blog:comment "文章《Redis入门》有新评论啦" - 客户端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消息通信场景的空白。