当前位置: 首页 > web >正文

Redis发布订阅:实时消息系统的极简解决方案

💡 一句话真相:Redis发布订阅就像"微信群发通知"💬——发布者(群主)发送消息,订阅者(群成员)实时接收,无需轮询,毫秒级延迟!


💥 一、为什么需要发布订阅?轮询的致命伤

传统轮询痛点:

在这里插入图片描述

问题:

  • 95%的请求是无效查询 ⏱
  • 高并发下服务器压力暴增 💥
  • 消息延迟高达数秒 ⏳

发布订阅破局:实时推送,0无效请求!


⚙️ 二、核心概念:三方角色解析

角色作用生活化比喻
发布者发送消息到指定频道新闻主播 📺
订阅者监听频道接收消息电视观众 👀
频道消息传输的通道电视频道 📡
发布消息
推送消息
推送消息
发布者
频道
订阅者1
订阅者2

📡 三、工作流程详解

1. 订阅者订阅频道
# 订阅news频道  
SUBSCRIBE news  

输出:

1) "subscribe"   # 订阅成功  
2) "news"        # 频道名  
3) (integer) 1   # 当前订阅数  
2. 发布者发送消息
# 向news频道发消息  
PUBLISH news "Redis 7.0 released!"  

返回值:接收到消息的订阅者数量

3. 订阅者实时接收
# 订阅者终端显示  
1) "message"        # 消息类型  
2) "news"           # 来源频道  
3) "Redis 7.0 released!"  # 消息内容  

🔧 四、五大进阶玩法

1. 模式订阅(匹配多个频道)
# 订阅所有tech_开头的频道  
PSUBSCRIBE tech_*  # 发布消息到tech_ai频道  
PUBLISH tech_ai "GPT-4震撼发布!"  
2. 取消订阅
UNSUBSCRIBE news     # 退订指定频道  
PUNSUBSCRIBE tech_*  # 退订模式频道  
3. 查看活跃频道
PUBSUB CHANNELS        # 所有活跃频道  
PUBSUB CHANNELS tech*  # 匹配tech*的频道  
4. 查看订阅者数量
PUBSUB NUMSUB news  # 查看news频道的订阅数  
5. 客户端阻塞监听
import redis  r = redis.Redis()  
pubsub = r.pubsub()  
pubsub.subscribe('news')  # 持续监听消息  
for message in pubsub.listen():  if message['type'] == 'message':  print(message['data'])  

🚀 五、四大应用场景实战

1. 实时聊天室

在这里插入图片描述

2. 系统事件通知
# 订单支付成功通知  
PUBLISH order_paid "订单ID:1001, 金额:299"  
3. 配置实时更新
# 配置中心发布更新  
redis.publish('config_update', '{"theme":"dark"}')  # 服务订阅更新  
def handle_config(message):  new_config = json.loads(message)  apply_config(new_config)  
4. 跨服务解耦
发布事件
推送事件
推送事件
服务A
Redis
服务B
服务C

⚖️ 六、发布订阅 vs 消息队列

特性发布订阅消息队列(Stream)
消息持久化❌ 消息不保存✅ 支持持久化
消费者组❌ 不支持✅ 支持
消息确认❌ 无ACK机制✅ 支持ACK
实时性⚡ 毫秒级⚡ 毫秒级
适用场景实时通知、广播任务队列、可靠传输

⚠️ 七、四大生产环境陷阱

🚫 陷阱1:消息丢失无保障

场景:订阅者掉线期间,消息全部丢失!
解决方案:

发布
发布者
Redis
Stream备份
订阅者
🚫 陷阱2:订阅者阻塞影响服务

错误代码:

while True:  message = pubsub.get_message()  # 死循环占用CPU  

优化方案:

for message in pubsub.listen():  # 阻塞式监听  process(message)  
🚫 陷阱3:频道爆炸性能下降

案例:创建10万个频道 → Redis内存暴增!
预防措施:

  • 使用模式订阅替代多个频道
  • 清理无效频道:CLIENT KILL TYPE pubsub
🚫 陷阱4:无权限控制

风险:任意客户端可订阅敏感频道(如payment
解决方案:

# 使用代理层鉴权  
location /pubsub {  auth_request /auth;  # 先验证权限  proxy_pass http://redis_backend;  
}  

📊 八、性能实测:百万级消息压力测试

场景消息大小QPS平均延迟
1万订阅者100B85,0000.3ms
10万订阅者100B42,0000.8ms
1万订阅者1KB68,0000.5ms
10万订阅者1KB32,0001.2ms

💡 测试环境:Redis 7.0,16核CPU/32GB内存,千兆网络


🔧 九、多语言实战代码

Python(redis-py)
import redis  # 发布者  
publisher = redis.Redis()  
publisher.publish('news', 'Python 3.12 released!')  # 订阅者  
def subscriber():  r = redis.Redis()  pubsub = r.pubsub()  pubsub.subscribe('news')  for message in pubsub.listen():  if message['type'] == 'message':  print(f"收到消息: {message['data']}")  # 启动订阅线程  
import threading  
threading.Thread(target=subscriber).start()  
Java(Jedis)
import redis.clients.jedis.Jedis;  
import redis.clients.jedis.JedisPubSub;  // 订阅者  
JedisPubSub listener = new JedisPubSub() {  @Override  public void onMessage(String channel, String message) {  System.out.println("收到: " + message);  }  
};  new Thread(() -> {  Jedis jedis = new Jedis("localhost");  jedis.subscribe(listener, "news");  
}).start();  // 发布者  
Jedis publisher = new Jedis("localhost");  
publisher.publish("news", "Java 21 LTS is out!");  
Node.js(ioredis)
const Redis = require('ioredis');  
const pub = new Redis();  
const sub = new Redis();  // 订阅  
sub.subscribe('news', (err) => {  if (!err) console.log('订阅成功!');  
});  sub.on('message', (channel, message) => {  console.log(`收到 ${channel} 消息: ${message}`);  
});  // 发布  
pub.publish('news', 'Node.js 20 released!');  

💎 十、总结:发布订阅适用三原则

  1. 实时性要求高:

    • 聊天消息
    • 实时监控报警
  2. 允许消息丢失:

    • 非关键通知(如在线人数更新)
    • 临时状态广播
  3. 无需复杂路由:

    • 广播场景
    • 简单一对一

在这里插入图片描述

🔥 黄金口诀:

  • 实时广播用PubSub,持久队列用Stream
  • 频道设计要规范,大小消息分得开
  • 生产环境加监控,异常退订及时查

#Redis发布订阅 #实时消息 #分布式系统

http://www.xdnf.cn/news/19068.html

相关文章:

  • 从数字到价值:ESG评级的深层变革
  • Linux827 测试
  • 计算机日常答疑,一起寻找问题的最优解
  • LeetCode算法日记 - Day 24: 颜色分类、排序数组
  • PyTorch图像预处理完全指南:从基础操作到GPU加速实战
  • 完整实验命令解析:从集群搭建到负载均衡配置(2)
  • [vcpkg] Windows入门使用介绍
  • day22 回溯算法part01
  • 服务器类型与TCP并发服务器构建(SELECT)
  • 设计模式:桥接模式(Bridge Pattern)
  • 《Linux内存管理:实验驱动的深度探索》【附录】【实验环境搭建 7】【使用buildroot方式构建文件系统】
  • 【开发便利】让远程Linux服务器能够访问内网git仓库
  • 链表-25.k个一组翻转链表-力扣(LeetCode)
  • 深入解析 Flink Function
  • Vue将内容生成为二维码,并将所有二维码下载为图片,同时支持批量下载(下载为ZIP),含解决一次性生成过多时页面崩溃解决办法
  • TCP 并发服务器构建
  • 智芯MCU 勘误文档问题解析
  • 【Java知识】Java线程相关对象全面解析与最佳实践
  • 阿里云——应用交付与负载均衡
  • 数据对话的“通用语法”:SQL与KingbaseES的智能处理艺术
  • 从感知机到大模型:神经网络的全景解析与实践指南
  • ES01-环境安装
  • 盛大启幕!融智兴科技亮相 IOTE 2025 深圳国际物联网展
  • SegEarth-R1: Geospatial Pixel Reasoning via Large Language Model
  • 稀土:从“稀有”到“命脉”的科技核心
  • LeetCode算法日记 - Day 23: 外观数列、数青蛙
  • LeetCode - 155. 最小栈
  • 8.28 模拟
  • rust语言(1.88.0)sqlite数据库rusqlite库(0.37.0)学习笔记
  • 蘑兔音乐:帮你把灵感落地