版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012658346/article/details/51417721
发布与订阅
redis的发布与订阅功能由以下几个命令实现:
序号 | 命令及描述 |
---|---|
1 | SUBSCRIBE 订阅给定的一个或多个频道的信息 |
2 | PUBLISH 将信息 message 发送到指定的频道 channel |
3 | PUBSUB 查看订阅与发布系统状态 |
4 | PSUBSCRIBE 订阅一个或多个符合给定模式的频道 |
使用示例
客户端A通过subscribe命令订阅频道“news”,客户端B通过命令publish发布“news”频道的信息,可以发现客户端A会收到这条信息
实现
redis将所有的订阅关系都保存在字典pubsub_channels中,这个字典的键key为被订阅的频道,对应的value是一个链表,指向所有订阅了这个channel的客户端
struct redisServer {
......
dict *pubsub_channels;
......
};
每个client也维护了一个这样的字典pubsub_channels,用来记录每个client所订阅的频道。这个字典的key为订阅的频道,value为NULL
typedef struct redisClient {
......
dict *pubsub_channels;
......
} redisClient;
假设有三个客户端:client A、client B、client C
其中A订阅了频道news和it,B订阅了频道news,C订阅了频道it
则一个基本的示意图如下:
server的字典中保存了两对key-value对,key为news时,对应的链表中有A和B两个client
key为it时,对应的链表中有A和C两个client。
client A的字典中保存着两个key-value对,key为news和it两个频道,value为NULL
订阅subscribe
void subscribeCommand(redisClient *c) {
for (j = 1; j < c->argc; j++) //依次处理每个订阅的频道
pubsubSubscribeChannel(c,c->argv[j]);
c->flags |= REDIS_PUBSUB;
}
int pubsubSubscribeChannel(redisClient *c, robj *channel) {
//将订阅的channel加入到对应client的字典中
if (dictAdd(c->pubsub_channels,channel,NULL) == DICT_OK) {
//在server的字典中查找channel
de = dictFind(server.pubsub_channels,channel);
//若server中不存在这个channel
if (de == NULL) {
//则创建一个链表,将channel-client加入到server中
clients = listCreate();
dictAdd(server.pubsub_channels,channel,clients);
} else {
//获取channel对应的client链表
clients = dictGetVal(de);
}
//将这个client加入到链表尾部
listAddNodeTail(clients,c);
}
//将信息返回给客户端
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.subscribebulk);
addReplyBulk(c,channel);
addReplyLongLong(c,clientSubscriptionsCount(c));
return retval;
}
发布publish
void publishCommand(redisClient *c) {
int receivers = pubsubPublishMessage(c->argv[1],c->argv[2]);
addReplyLongLong(c,receivers);
}
int pubsubPublishMessage(robj *channel, robj *message) {
//在server的字典中,找到channel
de = dictFind(server.pubsub_channels,channel);
if (de) {
//获取channel对应的client列表,遍历列表
list *list = dictGetVal(de);
while ((ln = listNext(&li)) != NULL) {
//依次向订阅了这个频道的所有client发送信息
redisClient *c = ln->value;
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.messagebulk);
addReplyBulk(c,channel);
addReplyBulk(c,message);
receivers++;
}
}
//向所有匹配模式的用户发送信息
if (listLength(server.pubsub_patterns)) {
listRewind(server.pubsub_patterns,&li);
channel = getDecodedObject(channel);
while ((ln = listNext(&li)) != NULL) {
pubsubPattern *pat = ln->value;
......
}
}
return receivers;
}
本文所引用的源码全部来自Redis3.0.7版本
redis学习参考资料:
https://github.com/huangz1990/redis-3.0-annotated
Redis 设计与实现(第二版)