Redis发布订阅


  • Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

  • 订阅者可以订阅任意数量的频道。

  • Redis被广泛用于构建即时通信应用,比如网络聊天室(chatroom)和实时广播、实时提醒等。

pubsub2

发布订阅的常用指令

序号命令及描述
1subscribe channel [channel ...] 订阅给定的一个或多个频道的信息。
2psubscribe pattern [pattern ...] 订阅一个或多个符合给定模式的频道。(模糊订阅)
3publish channel message 将信息发送到指定的频道。
4unsubscribe [channel [channel ...]] 指退订给定的频道。
5punsubscribe[pattern [pattern ...]] 退订所有给定模式的频道。(模糊退订)
6pubsub subcommand [argument [argument ...]] 查看订阅与发布系统状态。

发布订阅的实现

subscribe channel [channel …] 订阅频道

  • 当一个频道第一次被订阅时,这个频道就被自动创建。
# 1窗口
127.0.0.1:6379> subscribe chat news.nongye news.dili
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "chat"
3) (integer) 1
1) "subscribe"
2) "news.nongye"
3) (integer) 2
1) "subscribe"
2) "news.dili"
3) (integer) 3

psubscribe pattern [pattern …]订阅符合要求的频道

  • 通过*的方式来指定某个模式的频道,比如new.*来指定所有new.开头的频道。
# 2窗口
127.0.0.1:6379> psubscribe news.*		# 订阅具有new.开头的所有频道
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "news.*"
3) (integer) 1

publish channel message 发送信息到指定频道

向指定频道发送信息

# 3号窗口
127.0.0.1:6379> publish news.dili kuangshi
(integer) 2
127.0.0.1:6379> publish news.nongye daocao
(integer) 2
127.0.0.1:6379> publish chat hello
(integer) 1

订阅者接收信息

# 1号窗口
1) "message"
2) "news.dili"
3) "kuangshi"
1) "message"
2) "news.nongye"
3) "daocao"
1) "message"
2) "chat"
3) "hello"
# 2号窗口
1) "pmessage"
2) "news.*"
3) "news.dili"
4) "kuangshi"
1) "pmessage"
2) "news.*"
3) "news.nongye"
4) "daocao"

unsubscribe [channel [channel …]] 退订指定的频道

punsubscribe[pattern [pattern …]] 退订指定模式的频道

  • 当退出订阅发布模式时,会自动退订所有已订阅频道
  • unsubscribe命令不能应用于redis-cli客户端中,因为redis-cli运行订阅后,客户端处于阻塞模式,只能通过Ctrl-C退出订阅模式,而如果使用第三方组件则可以实现这一功能。
  • punsubscribe也是使用punsubscribe new.*的命令形式来使用的。
# 这里对unsubscribe进行一个简单的模拟
redis 127.0.0.1:6379> UNSUBSCRIBE mychannel 
1) "unsubscribe"
2) "a"
3) (integer) 0

pubsub subcommand [argument [argument …]] 查看订阅与发布系统状态

  • pubsub channels [pattern] 查看指定模式频道的活跃频道(至少要两以上订阅者)
  • pubsub numsub [channel-1 … channel-N] 查看指定频道的订阅者数量
  • pubsub numpat 查看频道数目
# 查看指定模式频道的活跃频道
127.0.0.1:6379> pubsub channels news.*
1) "news.dili"
2) "news.nongye"

# 查看指定频道的订阅者数量
127.0.0.1:6379> pubsub numsub news.dili news.nongye chat
1) "news.dili"
2) (integer) 2
3) "news.nongye"
4) (integer) 2
5) "chat"
6) (integer) 1

# 查看频道数目
127.0.0.1:6379> pubsub numpat
(integer) 3

订阅发布原理解析

publish.c文件夹存在着发布订阅模式的完整源码,这里对发布订阅作简单描述。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9sYUVtaWJIRnhGdzZKcm1IN3cwVnlCTm1yMzBnS3YzTk9nQm1pYjV4clBQejFkMDh4WUJpYldkVjQwcHhTbWlhRE5IQWhTTms0N3NrN0JZZlB0dzRwSUhkcncvNjQw

在 Redis 的底层结构中,Redis 服务器结构体中定义了一个 pubsub_channels 字典

struct redisServer {
	//用于保存所有频道的订阅关系
	dict *pubsub_channels;
}

这个字典中,key 代表的是频道名称,value 是一个链表,这个链表里面存放的是所有订阅这个频道的客户端。

// 订阅频道,传入订阅者和频道
int pubsubSubscribeChannel(client *c, robj *channel) {
    dictEntry *de;
    list *clients = NULLint retval = 0;

    /* Add the channel to the client -> channels hash table */
    // 更新字典的键值
    if (dictAdd(c->pubsub_channels,channel,NULL) == DICT_OK) {
        retval = 1;
        incrRefCount(channel);
        /* Add the client to the channel -> list of clients hash table */
        // 更新链表中的结点
        de = dictFind(server.pubsub_channels,channel);
        if (de == NULL) {
            clients = listCreate();
            dictAdd(server.pubsub_channels,channel,clients);
            incrRefCount(channel);
        } else {
            clients = dictGetVal(de);
        }
        listAddNodeTail(clients,c);
    }
    /* Notify the client */
    addReplyPubsubSubscribed(c,channel);
    return retval;
}
int pubsubPublishMessage(robj *channel, robj *message) {
    //传入频道和消息
    int receivers = 0;
    dictEntry *de;
    dictIterator *di;
    listNode *ln;
    listIter li;

    /* Send to clients listening for that channel */
    // 找到频道,遍历出订阅者并发布信息
    de = dictFind(server.pubsub_channels,channel);
    if (de) {
        list *list = dictGetVal(de);
        listNode *ln;
        listIter li;

        listRewind(list,&li);
        while ((ln = listNext(&li)) != NULL) {
            client *c = ln->value;
            addReplyPubsubMessage(c,channel,message);
            receivers++;
        }
    }
    /* Send to clients listening to matching channels */
    // 找到模式化订阅的订阅者,逻辑更为复杂
    di = dictGetIterator(server.pubsub_patterns);
    if (di) {
        channel = getDecodedObject(channel);
        while((de = dictNext(di)) != NULL) {
            robj *pattern = dictGetKey(de);
            list *clients = dictGetVal(de);
            if (!stringmatchlen((char*)pattern->ptr,
                                sdslen(pattern->ptr),
                                (char*)channel->ptr,
                                sdslen(channel->ptr),0)) continue;

            listRewind(clients,&li);
            while ((ln = listNext(&li)) != NULL) {
                client *c = listNodeValue(ln);
                addReplyPubsubPatMessage(c,pattern,channel,message);
                receivers++;
            }
        }
        decrRefCount(channel);
        dictReleaseIterator(di);
    }
    return receivers;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值