订阅发布
发送消息
当一个Redis客户端执行PUBLISH 命令将消息message发送给频道channel的时候,服务器需要执行以下
两个动作:
- 1.将消息message发送给channel频道的所有订阅者
- 2.如果一个或多个模式pattern与频道channel相匹配,那么将消息message发送给pattern模式的订阅者
将消息发送给频道订阅者
因为服务器状态中的pubsub_channels字典记录了所有频道的订阅关系,所以为了将消息发送给channel频道的所有订阅者,PUBLISH命令要做的就是在pubsub_channels字典里找到频道channel的订阅者名单(一个链表),
然后将消息发送给名单上的所有客户端
例子
- 举个例子。假设服务器pubsub_channels字典当前的状态如图所示,
如果这时某个客户端执行命令
PUBLISH "news.it" "hello"
那么PUBLISH命令将在pubsub_channels字典中查找键"news.it"对应的链表值,并通过遍历链表将消息"hello"发送给"news.it"频道的三个订阅者:client-1、client-2、client-3
PUBLISH命令实现
将消息发送给频道订阅者的方法可以用以下伪代码来描述:
def channel_publish(channel, message):
# 如果channel键不存在于pubsub_channels字典中
# 那么说明channel频道没有任何订阅者
# 程序不做发送动作,直接返回
if channel not in server.pubsub_channels:
return
# 运行到这里,说明channel频道至少有一个订阅者
# 程序遍历channel频道的订阅者链表
# 将消息发送给所有订阅者
for subscriber in server.pubsub_channels[channel]:
send_message(subscriber, message)
将消息发送给模式订阅者
因为服务器状态中的pubsub_patterns链表记录了所有模式的订阅关系,所以为了将消息发送给所有与channel频道相匹配的模式的订阅者,PUBLISH命令要做的就是遍历整个pubsub_patterns链表,查找那些与channel频道相匹配的模式,并将消息发送给订阅了这些模式的客户端。
例子
- 举个例子。假设pubsub_patterns链表的当前状态如图所示。
如果这时客户端执行命令
PUBLISH "news.it" "hello"
那么PUBLISH命令会首先将消息"hello"发送给"news.it"频道的所有订阅者,然后开始在pubsub_patterns链表中查找是否有被订阅的模式与"news.it"频道相匹配,结果发现"news.it"频道和客户端client-9订阅的"news.*"频道匹配,于是命令将消息"hello"发送给客户端client-9
PUBLISH命令
将消息发送给模式订阅者的方法可以用以下伪代码来描述:
def pattern_publish(channel, message):
# 遍历所有模式订阅消息
for pubsubPattern in server.pubsub_patterns:
# 如果频道和模式相匹配
if match(channel, pubsubPattern.pattern):
# 那么将消息发送给订阅该模式的客户端
send_message(pubsubPattern.client, message)
最后,PUBLISH命令的实现可以用以下伪代码来描述:
def publish(channel, message):
# 将消息发送给channel频道的所有订阅者
channel_publish(channel, message)
# 将消息发送给所有和channel频道相匹配的模式的订阅者
pattern_publish(channel, message)
查看订阅信息
PUBSUB命令是Redis2.8新增加的命令之一,客户端可以通过这个命令来查看频道或者模式的相关信息,比如某个频道目前有多少订阅者,
又或者某个模式目前有多少订阅者,诸如此类
PUBSUB CHANNELS
PUBSUB CHANNELS [pattern]子命令用于返回服务器当前被订阅的频道,其中pattern参数是可选的:
- 1.如果不给顶patter参数,那么命令返回服务器当前被订阅的所有频道
- 2.如果给定pattern参数,那么命令返回服务器当前被订阅的频道中那些与pattern模式相匹配的频道这个子命令是通过遍历服务器pubsub_channels字典的所有键(每个键都是一个被订阅的频道),然后记录
并返回所有符合条件的频道来实现额,这个过程可以用以下伪代码来描述
def pubsub_channels(pattern=None):
# 一个列表,用于记录所有符合条件的频道
channel_list = []
# 遍历服务器中的所有频道
# (也即是pubsub_channels字典的所有键)
for channel in server.pubsub_channels:
# 当以下两个条件的任意一个满足时,将频道添加到链表里面
# 1.用户没有指定pattern参数
# 2.用户指定了pattern参数,并且channel和pattern匹配
if (patter is None) or match (channel, pattern):
channel_list.append(channel)
# 向客户端返回频道列表
return channel_list
例子
- 举个例子。对于图中所示的pubsub_channels字典来说,执行PUBSUB CHANNELS命令将返回服务器目前订阅的四个频道:
1."news.it"
2."news.sport"
3."news.business"
4."news.movie"
另一方面,执行PUBSUB CHANNELS "news.[is]"命令将返回"news.it"和"news.sport"两个频道,因为只有这两个频道和"news[is]"模式相匹配
1."news.it"
2."news.sport"