《Redis源码学习笔记》发布/订阅

[url=http://diaocow.iteye.com/blog/1938032]《Redis源码学习笔记》文章列表[/url]

Redis的SUBSCRIBE命令,可以让客户端订阅任意数量的频道,每当有新消息发送到某个频道时,Redis就会把这消息发送给所有订阅该频道的客户端;如下图:客户端Client_1,Client_2,Client_3都订阅了频道channel,当有消息PUBLISH到频道channel时,这三个客户端都将收到消息:

[img]http://dl2.iteye.com/upload/attachment/0088/9463/1597f6bf-2f74-3e47-ba03-bb8ed9106399.png[/img]

[b]原理[/b]:RedisServer内部维护了一个pubsub_channels字典,其中字典的键就是被订阅的频道,而键值就是订阅该频道的客户端列表;

[img]http://dl2.iteye.com/upload/attachment/0088/9461/284e8452-4725-322d-9a83-2f15d939ab02.png[/img]

这样,当一个客户端执行PUBLISH channel_name命令时,Redis就可以根据channel_name在pubsub_channels中找到与其关联的客户端列表,然后把消息发送给它们,伪代码;
def publishCommand(channel, msg):
# 获取订阅channel的所有客户端列表
client_list = redisServer.pubsub_channels.get(channel)
if client_list is None: return
# 向每个客户端发送消息
for client in client_list:
client.sendMessage(msg)

另外,客户端自己也维护了一个pubsub_channels属性,用来记录自己订阅了哪些频道;同watched_keys属性一样(详情请参看 [url=http://diaocow.iteye.com/blog/1935092]事务[/url]章节),客户端维护这些也是出于效率考虑的:

a. 防止订阅相同的频道;
def subscribeCommand(client, channels):
for ch in channels:
# 如果已经订阅了该频道,则跳过
if ch in client.pubsub_channels: continue
# 把client添加到该频道关联的客户端列表
client_list = redisServer.pubsub_channels.get(ch)
client_list.add(client)
client.pubsub_channels.add(ch)

b. 在UNSUBSCRIBE时,可以快捷的取消该客户端订阅的所有频道,而无需遍历整个redisServer.pubsub_channels字典,伪代码:
def unsubscribeCommand(client):
# 获取client订阅的所有频道
channels = client.pubsub_channels
# 遍历频道
for ch in channels:
client_list = redisServer.pubsub_channels(ch)
# 从该频道关联的客户端列表中,删除client
client_list.del(client)
client.pubsub_channels.del(ch)


考虑这么一个需求:有两个频道,名字都以“hello_开头”,分别叫做hello_1, hello_2;当我们要订阅这类频道时,我们可能会这么写:SUBSCRIBE hello_1 hello_2,但是如果有100个难道要这样写 SUBSCRIBE hello_1 hello_2 ... hello_100? 这时候我们可以使用“[b]模式订阅[/b]”命令PSUBSCRIBE, 譬如这里我们就可以写成,PSUBSCRIBE hello_* ;这样,当一个客户端执行PUBLISH命令时,redis不仅会把消息发送给所有订阅该频道的客户端列表,同时也会把该频道与所有模式匹配,如果匹配成功,则把消息同样发送给订阅该模式的客户端列表:

[img]http://dl2.iteye.com/upload/attachment/0088/9466/31b9c2a8-eccc-37f6-b0c4-336bdd46be40.png[/img]

所以完整的PUBLISH命令伪代码如下:
def publishCommand(channel, msg):
# 获取订阅channel的所有客户端列表
client_list = redisServer.pubsub_channels.get(channel)
if client_list is None: return
# 向每个客户端发送消息
for client in client_list:
client.sendMessage(msg)

# 遍历pubsub_patterns
for pattern, client in redisServer.pubsub_patterns:
# 若模式与channel匹配,则把消息发送给订阅该模式的客户端
if pattern.match(channel):
client.sendMessage(msg)

更多细节请看:pubsub.c/publishCommand函数

[b]总结:[/b]
1. 熟悉发布订阅相关命令:subscribe/unsubscribe psubscribe/punsubscribe publish;
2. 了解发布订阅实现原理;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值