得益于出色的设计和优秀的性能,Redis 支持很多功能。其中一个便是发布/订阅pub/sub,让我们来看看如何使用这个功能吧!

发布/订阅模型

Redis 的 SUBSCRIBEUNSUBSCRIBEPUBLISH 这三个命令实现了一个基于发布订阅的消息模型。在这个模型中,消息发送者(发布者)并不会把消息发送给特定的消息接受者(订阅者)。已发布的消息会被描述为通道channel,发布者事先并不知道有哪些订阅者,甚至它们是否存在。同样,订阅者也不知道发布者的存在,只是选中一个或多个感兴趣的通道,然后只接收它们感兴趣的消息。在这个模型中,我们实现了发布者和订阅者的解耦,因此它能够实现更好的扩展性和更加动态的网络拓扑。

举个例子,若要订阅 foobar 通道,客户端需要发送一个 SUBSCRIBE 命令,并提供通道名称作为参数:

SUBSCRIBE foo bar

之后,当其他客户端把消息发送至这两个通道,Redis 就会把它们推送至所有订阅者客户端。

客户端若已经订阅了一个或多个通道,则它不应该发送命令,即使它可以订阅或取消订阅其他通道。订阅和取消订阅的响应也是以消息的方式发送的,这样一来,客户端就可以不间断地读取消息流,其中的第一个元素代表了消息类型。对于已经订阅通道的客户端来说,允许的命令有:SUBSCRIBESSUBSCRIBESUNSUBSCRIBEPSUBSCRIBEUNSUBSCRIBEPUNSUBSCRIBEPINGRESETQUIT

请注意,在已订阅模式下,redis-cli 不会接受任何命令,并且只能通过 Ctrl-C 来退出它。

推送消息的格式

每一条消息都是一个包含三个元素的数组。

其中,第一个元素是消息类型,它有以下三种可能:

  • subscribe:这代表我们成功订阅了某个通道,该通道由第二个参数给出,而第三个参数则代表了当前已订阅的通道总数。
  • unsubscribe:这代表我们成功取消订阅了某个通道,该通道由第二个参数给出。第三个参数则代表了当前已订阅的通道总数。如果最后一个参数是 0,这意味着当前没有订阅任何通道,那么客户端就可以发送任何 Redis 命令,因为当前不在发布/订阅状态。
  • message:这代表着当前消息真的是一条“消息”,是被其他客户端使用 PUBLISH 命令发送的“消息”。第二个元素是通道的名称。第三个参数是实际的消息负载payload(大小)。

数据库与作用域

发布/订阅功能和 key 空间没有关系,它是故意被设置成这样的。同时,它和数据库成员也没有任何关系。

在 db 10 上发布消息,在 db 1 上也可以被订阅者接收到。

如果你需要某种类型的作用域,你可以把环境名作为通道名的前缀,比如 test、staging、production 等。

协议示例

SUBSCRIBE first second
*3
$9
subscribe
$5
first
:1
*3
$9
subscribe
$6
second
:2

此时,我们在另一个客户端上,使用 PUBLISH 命令,向名为 second 的通道发送一条消息:

PUBLISH second Hello

第一个客户端会收到:

*3
$7
message
$6
second
$5
Hello

我们再让客户端取消订阅所有通道,使用不带参数的 UNSUBSCRIBE 就可以做到:

UNSUBSCRIBE
*3
$11
unsubscribe
$6
second
:1
*3
$11
unsubscribe
$5
first
:0

模式匹配订阅

Redis 的发布/订阅实现支持通道名的模式匹配。客户端或许会想要使用 Unix 通配符风格,来订阅名称所有符合特定模式的通道。

比如说:

PSUBSCRIBE news.*

将会接受所有发往 news.art.figurativenews.music.jazz 等通道的消息。所有 Unix 通配符风格的模式都是受支持的,因此多个通配符一起使用也行。

PUNSUBSCRIBE news.*

将会取消订阅所有名称符合该模式的通道。其他的订阅不会收到影响。

使用模式匹配订阅通道,而后接收到的消息的格式会有不同:

  • 消息类型为 pmessage:第二个参数是订阅的匹配模式,第三个参数是通道名称,最后一个参数是实际的消息负载(大小)。

PSUBSCRIBEPUNSUBSCRIBE 命令与 SUBSCRIBEUNSUBSCRIBE 命令相似,它们的消息格式的区别,也是体现在消息类型上,相应地变成了 pususcribepunsubscribe

同时匹配多个模式的消息

如果客户端订阅了多个模式匹配,或是它既订阅了模式匹配,又直接订阅了通道。那么,它有可能会重复收到同一条消息。

比如:

SUBSCRIBE foo
PSUBSCRIBE F*

在上述例子中,如果一条消息放松到 foo 通道,客户端会接收到两条消息:一条类型为 message,另一条类型为 pmessage

模式匹配中的订阅数

subscribeunsubscribepsubscribepunsubscribe 等消息类型中,最后一个参数是仍然存活的订阅数。这个数字是客户端仍订阅着的通道或模式的总数。因此,只有到这个数字变为 0,代表已经取消了所有订阅时,客户端才会退出发布/订阅状态。

共享的发布/订阅

Redis 7.0.0 版本引入。

(省略)

后语

下一篇文章中,我将介绍 Redis 分布式锁:SETNX 实现。请保持关注喔!

参考


本文使用 CC BY-SA 4.0 国际协议 进行许可,欢迎 遵照协议规定 转载。
作者:六开箱
链接:https://lkxed.github.io/posts/redis-pub-sub/