基于模式的发布与订阅实现方式主要通过以下三个命令:
* `psubscribe pattern-1 pattern-2`:订阅一个或者多个模式,模式可以通过通配符`?`和`*`来表示。
* `pubsubscribe pattern-1 pattern-1`:取消模式的订阅(基于命令操作,界面上无法退订)
* `publish channel-1 message`:向频道`channel-1`发送消息`message`。这里和上面基于频道命令是一样的。
打开一个客户端一,输入订阅命令`psubscribe m*`,表示当前客户端订阅了所有以`m`开头的频道:
![](https://img.kancloud.cn/5e/e2/5ee28d224bee0574042ad4c88726c888_588x127.png)
然后再打开一个客户端二,执行以下发布消息命令:
~~~sql
publish movie myCountry //发布消息 myCountry 到 movie 频道
publish music love //发布消息 love 到 music 频道
publish tv myHome //发布消息 myHome 到 tv 频道
~~~
![](https://img.kancloud.cn/50/38/5038121c22423329add70183b1fb5502_586x137.png)
前面两个频道发布之后返回`1`就表示当前有`1`个客户端订阅了该频道(上面基于频道订阅的客户端关闭之后会自动取消订阅,如果没有关闭的话这里前面两个就会返回`2`),消息已经发送到这个客户端。
这时候我们再回到之前的客户端一,就会发现客户端一收到了`myCountry`和`love`两条消息,因为这两个频道都是以`m`开头的,而`myHome`这条消息是属于频道`tv`,并不是以`m`开头,客户端一并没有订阅,故而不会收到:
![](https://img.kancloud.cn/72/85/7285044bc3f0d45170e86727af37101b_587x228.png)
同样的,基于模式的订阅也提供了一个查询命令:
* `pubsub numpat`:查询当前服务器被订阅模式的数量。
#### 实现原理分析
客户端与其订阅的模式信息被保存在`redisServer`对象中的`pubsub_patterns`属性中:
~~~c
struct redisServer {
list pubsub_patterns;//保存了客户端及其订阅的模式信息
//...省略其他信息
};
~~~
`pubsub_patterns`属性是一个列表,其列表内结构(源码`serer.h`内)定义如下:
~~~c
typedef struct pubsubPattern {
client *client;//订阅模式的客户端
robj *pattern;//被订阅的模式
} pubsubPattern;
~~~
![](https://img.kancloud.cn/54/a4/54a42f5f0ba0c31c0bbc1f019c6521f4_722x179.png)
* 订阅:新建一个`pubsubPattern`数据结构加入到链表`pubsub_patterns`的结尾。
* 取消订阅:从链表中将当前取消订阅的客户端`pubsubPattern`从链表`pubsub_patterns`中移除。
* 发送消息:此时需要遍历整个链表来寻找能匹配的模式。之所以基于模式场景使用链表是因为模式支持通配符,所以没有办法直接用字典实现。
PS:当基于频道和基于模式两种订阅同时都存在时,`Redis`会先去寻找频道字典,再去遍历模式链表进行消息发送。
- Redis 为什么这么快
- 什么是 Redis
- Redis 的安装
- Redis 到底有多快
- Redis 是单线程还是多线程
- Redis 为什么选择使用单线程来执行请求
- 什么是 IO 多路复用机制
- Redis 中 I/O 多路复用的应用
- 一个简单的字符串,为什么 Redis 要设计的如此特别
- Redis 的 9 种数据类型
- 二进制安全字符串
- sds 空间分配策略
- sds 和 C 语言字符串区别
- sds 是如何被存储的
- type 属性
- encoding 属性
- 通过牺牲速度来节省内存,Redis 是觉得自己太快了吗
- 什么是压缩列表
- ziplist 的存储结构
- entry 存储结构
- ziplist 数据示例
- ziplist 连锁更新问题
- 为了加快速度,Redis 都做了哪些“变态”设计
- 列表对象
- linkedlist
- linkedlist 和 ziplist 的选择
- quicklist
- 列表对象常用操作命令
- Redis 中哈希分布不均匀该怎么办
- 哈希对象
- hashtable
- ziplist
- ziplist 和 hashtable 的编码转换
- 哈希对象常用命令
- 同一份数据,Redis 为什么要存”两次”
- 五种基本类型之集合对象
- intset 编码
- 集合对象常用命令
- 五种基本类型之有序集合对象
- skiplist 编码
- ziplist 编码
- ziplist 和 skiplist 编码转换
- 有序集合对象常用命令
- 要想用活 Redis,Lua 脚本是绕不过去的坎
- 发布与订阅
- 基于频道的实现
- 基于模式的实现
- Lua 脚本
- Lua 脚本的调用
- Lua 脚本中执行 Redis 命令
- Lua 脚本摘要
- Lua 脚本文件
- 脚本异常
- 作为一款内存数据库,为什么断电后 Redis 数据不会丢失
- Redis 持久化机制
- RDB 持久化机制
- AOF 持久化机制
- 内存耗尽后 Redis 会发生什么
- 内存回收
- 过期策略
- 8 种淘汰策略
- LRU 算法
- LFU 算法
- 不能回滚的 Redis 事务还能用吗
- Redis 有事务吗
- Redis 事务实现原理
- Redis 事务 ACID 特性
- watch 命令
- watch 命令的作用
- watch 原理分析
- Redis 为什么不直接用 master-slave 集群
- Redis 集群方案
- 主从复制
- 配置一主两从 master-slave 集群
- 主从复制原理分析
- 主从服务的不足之处
- Sentinel(哨兵)机制为什么从神坛滑落
- 哨兵 Sentinel 机制
- Sentinel 原理分析
- 配置 Sentinel 集群
- Sentinel 机制实战
- Sentinel 机制的不足之处
- Redis Cluster 集群凭什么成为了最终的胜利者
- Redis 分布式集群方案
- 客户端实现分片
- 中间代理服务实现分片
- Redis Cluster 方案
- 手动配置一个 Redis Cluster 集群
- Redis Cluster 集群常用命令
- 客户端如何使用 Redis Cluster 集群
- Redis Cluster 的不足
- 如何从 10 亿数据中快速判断是否存在某一个元素
- 缓存雪崩
- 缓存击穿
- 缓存穿透
- 布隆过滤器(Bloom Filter)
- 布隆过滤器的 2 大特点
- 布隆过滤器的实现(Guava)
- 布隆过滤器的如何删除