Redis发布订阅原理与实践

幽沉谢世事,俯默窥唐虞。这篇文章主要讲述Redis发布订阅原理与实践相关的知识,希望能为你提供帮助。
简述Redis提供了发布订阅功能,可以用于消息的传输, 通过 PUBLISH 、 SUBSCRIBE 和 PSUBSCRIBE 等命令实现发布和订阅功能
分类频道的发布订阅
基于Channel的消息事件,这一类消息和Redis中存储的Keys没有太多关联,这类消息事件是独立使用(PUBLISH,SUBSCRIBE)
实现原理Redis将所有频道的订阅关系都保存在服务器状态的 pubsub_channels 字典,字典的键是某个被订阅的频道,而对应值则是一个链表,链表里记录了所有订阅这个频道的客户端。

struct redisServer
//...

// 保存所有频道订阅关系
dict *pubsub_channels; /* Map channels to list of subscribed clients */

//...

一个pubsub_channels字典示例如下:

client-1、client-2三个客户端正在订阅 “redis” 频道
客户端 client-3 正在订阅 “mysql” 频道
订阅频道命令
SUBSCRIBE redis [mysql …]

频道有订阅者
在pubsub_channels字典中已存在该频道,此时server端会将订阅者客户端添加到该频道的链表末端
频道无订阅者
pubsub_channels字典中无此频道,在字典中创建频道,并此客户端放至该频道链表的第一位
应用实例1、订阅普通频道或多频道
127.0.0.1:6379> SUBSCRIBE redis mysql
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redis"
3) (integer) 1
1) "subscribe"
2) "mysql"
3) (integer) 2
1) "message"
2) "redis"
3) "1"
1) "message"
2) "mysql"
3) "1"

模式的发布订阅
消息事件主要是对Redis中存储的Keys信息的变化事件进行通知,可以用来向订阅者通知Redis中符合订阅条件的Keys的各种事件(需要redist开启notify-keyspace-events参数,PSUBSCRIBE订阅),还可以通过Channel订阅消息,模式订阅psubscribe的Channel订阅和subscribe命令类似。
实现原理Redis将所有模式的订阅关系都保存在服务器状态的 pubsub_patterns 链表,链表的每个节点都包含着一个 pubsub Pattern 结构,这个结构的 pattern 属性记录了被订阅的模式,而 client 属性则记录了订阅模式的客户端。
struct redisServer

//...

// 保存所有模式订阅关系
list *pubsub_patterns; /* A list of pubsub_patterns */
dict *pubsub_patterns_dict; /* A dict of pubsub_patterns */
int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an
xor of NOTIFY_... flags. */

//...

一个pubsub_patterns链表示例:

客户端 client-7 正在订阅模式 "redis*"
客户端 client-8 正在订阅模式 "mysql*"
订阅模式命令
PSUBSCRIBE pattern [pattern …]

【Redis发布订阅原理与实践】当客户端执行PSUBSCRIBE。redis server端会做如下操作
1、新建一个 pubsubPattern结果,将结构的 pattern 属性设置为被订阅的模式,client 属性设置为订阅模式的客户端;
2、将pubsubPattern结构添加到 pubsub_patterns 链表的尾部
应用举例
  • 订阅模糊频道
127.0.0.1:6379> PSUBSCRIBE redis* mysql*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "redis*"
3) (integer) 1
1) "psubscribe"
2) "mysql*"
3) (integer) 2
1) "pmessage"
2) "mysql*"
3) "mysql2"
4) "1"
1) "pmessage"
2) "redis*"
3) "redis2"
4) "1"

  • 订阅Redis key事件
需redis server端开启notify-keyspace-events参数,参数含义如下:
K:启用keyspace键空间通知,客户端可以使用__keyspace@< db> __为前缀的格式使用订阅功能。
E:启用keyevent键事件通知,客户端可以使用__keyevent@< db> __为前缀的格式使用订阅功能。
g:监控一般性事件,包括但不限于对del,expire,rename事件的监控。
$:启用对字符串格式(即一般K-V结构)命令的监控。
l:启用对List数据结构命令的监控。
s:启用对Set数据结构命令的监控。
h:启用对Hash数据结构命令的监控。
z:启用对ZSet数据结构命令的监控。
x:启用对过期事件的监控。
e:启用对驱逐事件的监控,当某个键因maxmemory达到设置时,使用策略进行内存清理,会产生这个事件。
A:g$lshzxe通配符组合的别名,也就是说”AKE”这样的通配符组合,意味着所有事件。

举例说明
// 监控任何数据格式的所有事件,包括键空间通知和键事件通知
notify-keyspace-events "AKE"

// 只监控字符串结构的所有事件,包括键空间通知和键事件通知
notify-keyspace-events "g$KExe"

// 只监控所有键事件通知
notify-keyspace-events "AE"

// 只监控Hash数据解构的键空间通知
notify-keyspace-events "ghKxe"

// 只监控Set数据结构的键事件通知
notify-keyspace-events "gsExe"

(1)订阅0号数据库中,所有的键变化事件,进行键空间通知
psubscribe __keyspace@0__:*

(1)订阅0号数据库中,所有的键变化事件,进行键事件通知
psubscribe __keysevent@0__:*

(2) 订阅0号数据库,test_redis_模糊键变化事件,进行键空间通知和键事件通知
psubscribe __key*@0__:test_redis_*

(3)订阅0号数据库中,模糊的键变化事件,进行键空间通知
psubscribe __key*@0__:*

相关命令
  • 查看所有的频道:PUBSUB CHANNELS
  • 查询订阅者的数量:PUBSUB NUMSUB
  • 查询服务器被订阅者的数量:PUBSUB NUMPAT
不足之处
  • Redis无法对消息持久化存储,一旦消息被发送,如果没有订阅者接收,那么消息就会丢失
  • Redis没有提供消息传输保障,当客户端连接超时等情况发生时,消息不会被重新发送给客户端
  • 订阅过期key时,只有当key被真正清理才会被发送道订阅通道,ttl 为0 不会被发送(这种情况一般发生在大批量的key过期集中在一个时间,redis负载相对较高高)
  • 消息位广播模式,订阅的客户端都会收到相同的消息,客户端会有重复消费问题

    推荐阅读