Redis 发布订阅
Redis 发布订阅
思维导图
生活化场景重现
学校时期,学级主任为了提高整个学级学生的写作能力,会要求我们订阅一些周刊例如《读者》《意林》。
简单模式:
同学各自订阅,然后出版商会把周刊递交给邮局【频道channel】,然后邮局会在递交给所订阅整个周刊的同学们【(订阅者/客户端)client】
复杂模式:
出资班费后班级【频道channel】订阅,然后这个周刊,班级内的同学【(订阅者/客户端)client】都可以阅读
底层实现
服务器将所有的订阅关系都保存在服务器状态的pubsub_channels
属性里面,在模式订阅里服务器也将所有模式的订阅关系都保存在服务器状态的pubsub_patterns
属性里面。
struct redisServer {
// ...
// 保存所有模式订阅关系
list *pubsub_patterns;
// ...
}
pubsub_patterns
属性是一个链表,链表中的每个节点都包含着一个pubsubPattern
结构,这个结构的pattern
属性记录了被订阅的模式,而client
属性则记录了订阅模式的客户端。
typedef struct pubsubPattern {
// 订阅模式的客户端
redisClient *client;
// 被订阅的模式
robj * pattern;
} pubsubPattern;
每当客户端执行PSUBSCRIBE
命令订阅某个或某些模式的时候,服务器会对每个被订阅的模式执行以下两个操作:
-
新建一个
pubsubPattern
结构,将结构的pattern
属性设置为被订阅的模式(如:music.*
),client
属性设置为订阅模式的客户端。 -
将
pubsubPattern
结构添加到pubsub_patterns
链表的表尾。
实战演练 Gif动画
第一层窗口:发布订阅窗口
第二层窗口:1:订阅 it.new 频道、2:订阅 it.photo 频道
第三层窗口:1:订阅 it.* 模式频道、2: 订阅 it* 模式频道
应用场景
简单的应用场景的话, 以门户网站为例, 当编辑更新了某推荐板块的内容后:
CMS发布清除缓存的消息到channel (推送者推送消息)
门户网站的缓存系统通过channel收到清除缓存的消息 (订阅者收到消息),更新了推荐板块的缓存
Redis 发布订阅缺陷
作为我个人角度来看这个redis发布订阅,觉得挺鸡肋的,首先客户端必须得订阅,发布端才能发布信息,否则发布信息就报异常的错误。还有客户端一直要保持在线才能收到消息,和设计模式中的观察者模式极为相似。 。所以总结一下,redis订阅模式有以下缺陷
1、redis系统的稳定性有关。对于旧版的redis来说,如果一个客户端订阅了某个或者某些频道,但是它读取消息的速度不够快,那么不断的积压的消息就会使得redis输出缓冲区的体积越来越大,这可能会导致redis的速度变慢,甚至直接崩溃。也可能会导致redis被操作系统强制杀死,甚至导致操作系统本身不可用。新版的redis不会出现这种问题,因为它会自动断开不符合client-output-buffer-limit pubsub配置选项要求的订阅客户端
2、数据传输的可靠性。任何网络系统在执行操作时都可能会遇到断网的情况。而断线产生的连接错误通常会使得网络连接两端中的一端进行重新连接。如果客户端在执行订阅操作的过程中断线,那么客户端将会丢失在断线期间的消息,这在很多业务场景下是不可忍受的。
所以如果我碰到类似的业务的话,我也不会选择redis的发布订阅模式,我会选择:rabbitMQ,rockedMQ,activitedMQ 等消息中间件
常用命令
下表列出了 redis 发布订阅常用命令:
序号 | 命令及描述 |
---|---|
1 | PSUBSCRIBE pattern [pattern ...] 订阅一个或多个符合给定模式的频道。 |
2 | PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态。 |
3 | PUBLISH channel message 将信息发送到指定的频道。 |
4 | PUNSUBSCRIBE [pattern [pattern ...]] 退订所有给定模式的频道。 |
5 | SUBSCRIBE channel [channel ...] 订阅给定的一个或多个频道的信息。 |
6 | UNSUBSCRIBE [channel [channel ...]] 指退订给定的频道。 |