Redis(8)——发布/订阅与Stream

本文详细介绍了Redis中的发布/订阅功能,包括PubSub的基本概念、快速体验、实现原理和缺点。此外,还深入探讨了Redis Stream这一持久化的发布/订阅系统,讲解了消息ID、消息内容、增删改查示例以及Stream与Kafka的对比。文章旨在帮助读者理解Redis PubSub和Stream的使用和原理。
摘要由CSDN通过智能技术生成

一、Redis 中的发布/订阅功能

发布/ 订阅系统 是 Web 系统中比较常用的一个功能。简单点说就是 发布者发布消息,订阅者接受消息,这有点类似于我们的报纸/ 杂志社之类的: (借用前边的一张图)

从我们 前面(下方相关阅读) 学习的知识来看,我们虽然可以使用一个 list 列表结构结合 lpushrpop 来实现消息队列的功能,但是似乎很难实现实现 消息多播 的功能:

为了支持消息多播,Redis 不能再依赖于那 5 种基础的数据结构了,它单独使用了一个模块来支持消息多播,这个模块就是 PubSub,也就是 PublisherSubscriber (发布者/ 订阅者模式)

PubSub 简介

我们从 上面的图 中可以看到,基于 list 结构的消息队列,是一种 PublisherConsumer 点对点的强关联关系,Redis 为了消除这样的强关联,引入了另一种概念:频道 (channel)

Publisherchannel 中发布消息时,关注了指定 channelConsumer 就能够同时受到消息。但这里的 问题 是,消费者订阅一个频道是必须 明确指定频道名称 的,这意味着,如果我们想要 订阅多个 频道,那么就必须 显式地关注多个 名称。

为了简化订阅的繁琐操作,Redis 提供了 模式订阅 的功能 Pattern Subscribe,这样就可以 一次性关注多个频道 了,即使生产者新增了同模式的频道,消费者也可以立即受到消息:

例如上图中,所有 位于图片下方的 Consumer 都能够受到消息

Publisherwmyskxz.chat 这个 channel 中发送了一条消息,不仅仅关注了这个频道的 Consumer 1Consumer 2 能够受到消息,图片中的两个 channel 都和模式 wmyskxz.* 匹配,所以 Redis 此时会同样发送消息给订阅了 wmyskxz.* 这个模式的 Consumer 3 和关注了在这个模式下的另一个频道 wmyskxz.log 下的 Consumer 4Consumer 5

另一方面,如果接收消息的频道是 wmyskxz.chat,那么 Consumer 3 也会受到消息。

快速体验

Redis 中,PubSub 模块的使用非常简单,常用的命令也就下面这么几条:

# 订阅频道:
SUBSCRIBE channel [channel ....]   # 订阅给定的一个或多个频道的信息
PSUBSCRIBE pattern [pattern ....]  # 订阅一个或多个符合给定模式的频道
# 发布频道:
PUBLISH channel message  # 将消息发送到指定的频道
# 退订频道:
UNSUBSCRIBE [channel [channel ....]]   # 退订指定的频道
PUNSUBSCRIBE [pattern [pattern ....]]  #退订所有给定模式的频道

我们可以在本地快速地来体验一下 PubSub

具体步骤如下:

  1. 开启本地 Redis 服务,新建两个控制台窗口;
  2. 在其中一个窗口输入 SUBSCRIBE wmyskxz.chat 关注 wmyskxz.chat 频道,让这个窗口成为 消费者
  3. 在另一个窗口输入 PUBLISH wmyskxz.chat 'message' 往这个频道发送消息,这个时候就会看到 另一个窗口实时地出现 了发送的测试消息。

实现原理

可以看到,我们通过很简单的两条命令,几乎就可以简单使用这样的一个 发布/ 订阅系统 了,但是具体是怎么样实现的呢?

每个 Redis 服务器进程维持着一个标识服务器状态redis.h/redisServer 结构,其中就 保存着有订阅的频道 以及 订阅模式 的信息:

struct redisServer {
   
    // ...
    dict *pubsub_channels;  // 订阅频道
    list *pubsub_patterns;  // 订阅模式
    // ...
};

订阅频道原理

当客户端订阅某一个频道之后,Redis 就会往 pubsub_channels 这个字典中新添加一条数据,实际上这个 dict 字典维护的是一张链表,比如,下图展示的 pubsub_channels 示例中,client 1client 2 就订阅了 channel 1,而其他频道也分别被其他客户端订阅:

SUBSCRIBE 命令

SUBSCRIBE 命令的行为可以用下列的伪代码表示:

def SUBSCRIBE(client, channels):
    # 遍历所有输入频道
    for channel in channels:
        # 将客户端添加到链表的末尾
        redisServer.pubsub_channels[channel].append(client)

通过 pubsub_channels 字典,程序只要检查某个频道是否为字典的键,就可以知道该频道是否正在被客户端订阅;只要取出某个键的值,就可以得到所有订阅该频道的客户端的信息。

PUBLISH 命令

了解 SUBSCRIBE,那么 PUBLISH 命令的实现也变得十分简单了,只需要通过上述字典定位到具体的客户端,再把消息发送给它们就好了:(伪代码实现如下)

def PUBLISH(channel, message):
    # 遍历所有订阅频道 channel 的客户端
    for client in server.pubsub_channels[channel]:
        # 将信息发送给它们
        send_message(client, message)
UNSUBSCRIBE 命令

使用 UNSUBSCRIBE 命令可以退订指定的频道,这个命令执行的是订阅的反操作:它从 pubsub_channels 字典的给定频道(键)中,删除关于当前客户端的信息,这样被退订频道的信息就不会再发送给这个客户端。

订阅模式原理

正如我们上面说到了,当发送一条消息到 wmyskxz.chat 这个频道时,Redis 不仅仅会发送到当前的频道,还会发送到匹配于当前模式的所有频道,实际上,pubsub_patterns 背后还维护了一个 redis.h/pubsubPattern 结构:

typedef struct pubsubPattern {
   
    redisClient *client;  // 订阅模式的客户端
    robj *pattern;        // 订阅的模式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值