redis 发布和订阅实现

参考文献

  1. 15天玩转redis —— 第九篇 发布/订阅模式
  2. 《Redis设计与实现》

命令简介

在redis用户手册中,跟发布订阅相关的命令有如下的六个:

PSUBSCRIBE
PUBLISH
PUBSUB
PUNSUBSCRIBE
SUBSCRIBE
UNSUBSCRIBE

接下来一一简述下这个6个命令的相关使用。

subscribe

命令参考链接:http://redisdoc.com/pub_sub/subscribe.html

SUBSCRIBE channel [channel ...]

订阅给定的一个或多个频道的信息。

SUBSCRIBE命令可以订阅一个频道。当有任何一个客户端向这个频道推送消息的时候,订阅了这个频道的客户端都可以收到数据。

Psubscribe

命令参考链接:http://redisdoc.com/pub_sub/psubscribe.html

如上所述,客户端可以订阅一个频道的消息,那么我是否可以订阅满足一定条件的频道的所有消息呢?redis提供了Psubscribe命令,这个命令可以订阅符合模式的频道的消息。每个模式以 * 作为匹配符,比如 it* 匹配所有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等), news.* 匹配所有以 news. 开头的频道( news.it 、 news.global.today 等等),诸如此类。

PSUBSCRIBE pattern [pattern ...]

UNSUBSCRIBE

命令参考链接:http://redisdoc.com/pub_sub/unsubscribe.html

我订阅了一个频道,也有需求退订。UNSUBSCRIBE命令就提供了客户端退订一个渠道的功能。

UNSUBSCRIBE [channel [channel ...]]

PUNSUBSCRIBE

命令参考链接:http://redisdoc.com/pub_sub/psubscribe.html

同理,如果我通过某个模式订阅了频道,我今天有点小后悔想退订这个模式咋办?使用PUNSUBSCRIBE命令就可以达成这个功能。

publish

命令参考链接:http://redisdoc.com/pub_sub/publish.html

publish命令可以向一个频道推送消息。如上面说到的,如果使用publish命令向一个频道推送消息,则所有订阅了这个频道的客户端都可以收到这个消息。

PUBLISH channel message

PUBSUB

命令参考链接:http://redisdoc.com/pub_sub/pubsub.html

上面都是关于订阅和退订相关的功能的简述。如果我是管理redis的一个DBA,需要查看当前有多少个活跃的频道(活跃频道指的是那些至少有一个订阅者的频道),每个频道有多少的订阅者,有多少模式订阅的相关信息怎么办呢?redis给我们提供了PUBSUB命令。

PUBSUB <subcommand> [argument [argument ...]]
  1. PUBSUB CHANNELS [pattern]:列出当前的活跃频道。活跃频道指的是那些至少有一个订阅者的频道,订阅模式的客户端不计算在内。

pattern 参数是可选的:
如果不给出 pattern 参数,那么列出订阅与发布系统中的所有活跃频道。
如果给出 pattern 参数,那么只列出和给定模式 pattern 相匹配的那些活跃频道。

  1. PUBSUB NUMSUB [channel-1 ... channel-N] : 返回给定频道的订阅者数量,订阅模式的客户端不计算在内。

  2. PUBSUB NUMPAT : 返回订阅模式的数量。注意,这个命令返回的不是订阅模式的客户端的数量,而是客户端订阅的所有模式的数量总和。

实现

前面的篇幅主要介绍了redis提供的发布和订阅功能,接下来将介绍下发布和订阅功能具体是怎么实现的。

订阅/退订一个频道

当你在Redis客户端键入SUBSCRIBE的时候,就会建立一个客户端与一个频道的订阅关系。那么Redis是通过怎样的形式来完成这个关联的呢?在Redis中,将所有的频道和客户端的订阅关系都保存在一个叫puhsub_channels的字典中。这个字典的key是是某个频道,而values则是一个链表,记录了所有订阅这个频道的客户端。

image

struct redisServer{
    dict * pubsub_channels;
}

因此当建立一个客户端和频道的订阅关系的时候,大体会经历如下的两步操作:

  1. 如果该频道已经存在于pubsub_channels中,则将客户端加到这个链表的后面。
  2. 如果这个频道不存在,则在字典中建立一个新的频道,然后将客户端添加到链表中。

退订的操作则和订阅的操作相反:

  1. 根据退订的频道是哪个,将链表上对应的客户端删除。
  2. 如果删除了客户端之后,整个链表为空,则表示这个频道没有被订阅,因此这个频道也将被删除 。

模式订阅和退订

前面的部分说到了一般的订阅和退订,接下来将介绍Redis模式的订阅是如何实现的。在Redis中所有的模式的订阅的关系都保存在服务器的pubsub_patterns 属性中。这个属性是一个链表结构。

struct redisServer{
    list * pubsub_patterns;
}

链表结构中每一个节点都包含了一个pubsub_pattern 结构。

image

typedef struct pubsubPattern {
    redisClient * client ;
    robj * pattern;
}pubsubPattern;

当客户端执行模式的订阅命令的时候,服务器会执行以下的几个操作:

  1. 新建一个pubsubPattern,将结构的pattern设置为被订阅的模式,client设置为订阅这个模式的客户端。
  2. 然后将这个结构体挂到pubsub_patterns的尾部。

退订的操作则刚好相反:

  1. 遍历pubsub_pattern,找到客户端退订的模式,将这个链表的节点删除

发送消息给频道订阅者

如前面的章节所述,频道的订阅是通过pubsub_channels结构实现的。那么当推送一个消息给频道的订阅者的时候,则需要执行如下的几个操作:

  1. 根据频道的名称在dict pubsub_channels中找到对应的频道
  2. 遍历这个频道上所有的订阅者名单,将信息挨个发送给订阅者

发送消息给模式订阅者

那么模式的订阅的消息发送是如何实现的呢?其实实现的原理就更简单了,Redis会遍历pubsub_patterns上所有的链表节点,找到匹配的模式,然后发送消息。

猜你喜欢

转载自www.cnblogs.com/bush2582/p/9440672.html