引言
发布订阅模型是redis的重要功能,它可以像网站动态一样,将消息发送到多个订阅者的主页里。
一、常用命令
二、消息格式
消息是一个有三个元素的多块响应:
如上图,发布者向 mysub 频道发送了一条消息,redis会返回当前订阅者数量。
而订阅者这边,当第一次订阅时,响应块分为 3 行,
第一行表示消息类型,subscribe :订阅成功 ;unsubscribe:表示取消订阅;message :表示这个响应块是一个发布的消息
第二行表示频道名称
第三行根据消息类型不同,subscribe/unsubscribe 时会响应目前订阅频道数量,message 时会响应具体消息内容。
注意:发布订阅与key所在空间没有任何关系,它不受任何级别的干扰,例如,db1可以订阅db10的发布消息。如果需要区分某些频道,只能使用频道名称来区分,比如在频道名称前加上环境信息:dev-xxx,test-xxx,prod-xxxx等等。
三、模式匹配订阅
psubscribe 配合通配符来匹配多个频道,如下命令可以匹配到 dev.msg 、dev.tag.other 等符合 dev.* 模式的全部频道:
PSUBSCRIBE dev.*
当作模式匹配结果的消息会以不同的格式发送:
消息类型是pmessage,这是另一客户端发出的PUBLISH命令的结果,匹配一个模式匹配订阅。第一个元素是原匹配的模式,第三个元素是原频道名称,最后一个元素是实际消息内容。
如果既使用了模式匹配,又使用了普通的订阅方式,例如:
subscribe foo
psubscribe f*
那么如果一个消息被发布到 foo,客户端会接收到两条消息:一条是 message 类型,另一条是 pmessage类型。
四、发布订阅的使用场景与注意事项
Redis的发布订阅功能可以应用到哪些场景?聊天室?网站用户的动态发布?
首先,需要注意一个问题,当客户端订阅了一个频道后,那么它只能接收到订阅之后发布的消息,也就是说,发布的消息是实时的。
因此,我们可以用它来做一个简易聊天室,或弹幕功能。但面对更复杂一些的场景,例如,如果想在客户端加载以前发布的消息又该如何解决这个问题呢?
这里的需求是,客户端即可以接受实时消息,又可以通过某些操作获取到订阅前的历史消息,如何实现?
这时,我们就可以结合redis的其他缓存功能,例如 ZSet 来存储 3天内的发布消息,再往前的数据就持久化到数据库。
这样,我们即实现了实时的订阅发布,也细分了历史数据——三天内和更久的数据:
对于三天内的数据,如何存放在ZSet中?有两种方案,需要根据业务具体考虑,要么是根据时间,要么是根据数据数量,比如某些数据就是只需要存100条。
按时间缓存,如上图中的 3 天,是比较常见的常见,我们可以在 socre 中标记日期,以此来维持数据的容量,参考使用 zremrangebyscore(按分数,即时间戳)或zremrangebyrank(按数据量,如最后100条)。
那么具体一点的实现模型就可以是这样: