SUBSCRIBE
、UNSUBSCRIBE
和 PUBLISH
实现了发布/订阅消息范式,在该范式中,发布者将消息发送到频道,而无需知道可能存在哪些接收者(订阅者)。订阅者表达对一个或多个频道的兴趣,并且只接收其感兴趣的消息,而无需知道存在哪些(如果存在的话)发布者。发布者和订阅者之间的这种解耦允许更大的可伸缩性和更动态的网络拓扑。
发布/订阅频道不持久化。如果发布消息时频道没有订阅者,则消息会丢失。如果您正在寻找持久化的消息队列,您可能需要查看Streams。
要订阅频道“channel11”和“ch:00”,客户端会发出一个 SUBSCRIBE
命令,并提供频道名称
SUBSCRIBE channel11 ch:00
其他客户端发送到这些频道的消息将由 Valkey 推送给所有订阅的客户端。订阅者按照消息发布的顺序接收消息。
订阅了一个或多个频道的客户端不应发出命令,尽管它可以 SUBSCRIBE
和 UNSUBSCRIBE
其他频道。订阅和取消订阅操作的回复以消息形式发送,以便客户端只需读取一个连贯的消息流,其中第一个元素指示消息的类型。在已订阅的 RESP2 客户端上下文中允许的命令是:
PING
PSUBSCRIBE
PUNSUBSCRIBE
QUIT
RESET
SSUBSCRIBE
SUBSCRIBE
SUNSUBSCRIBE
UNSUBSCRIBE
然而,如果使用 RESP3(参见 HELLO
),客户端在订阅状态下可以发出任何命令。
请注意,在使用 valkey-cli
时,在订阅模式下,`UNSUBSCRIBE` 和 `PUNSUBSCRIBE` 等命令无法使用,因为 `valkey-cli` 不会接受任何命令,只能通过 `Ctrl-C` 退出该模式。
传递语义
Valkey 的发布/订阅表现出至多一次的消息传递语义。顾名思义,这意味着消息如果被传递,则只会被传递一次。一旦 Valkey 服务器发送了消息,就没有机会再次发送。如果订阅者无法处理消息(例如,由于错误或网络断开),消息将永远丢失。
如果您的应用程序需要更强的传递保证,您可能需要了解Streams。Streams 中的消息是持久化的,并且支持至多一次和至少一次的传递语义。
推送消息的格式
消息是一个包含三个元素的数组回复。
第一个元素是消息类型
-
subscribe
:表示我们已成功订阅回复中作为第二个元素给出的频道。第三个参数表示我们当前订阅的频道数量。 -
unsubscribe
:表示我们已成功取消订阅回复中作为第二个元素给出的频道。第三个参数表示我们当前订阅的频道数量。当最后一个参数为零时,我们不再订阅任何频道,并且客户端可以发出任何类型的 Valkey 命令,因为我们已经离开了发布/订阅状态。 -
message
:这是另一个客户端发出的PUBLISH
命令所接收到的消息。第二个元素是源频道的名称,第三个参数是实际的消息负载。
数据库与范围
发布/订阅与键空间无关。它旨在不以任何级别干扰键空间,包括数据库编号。
在 db 10 上发布,将在 db 1 上的订阅者收到。
如果您需要某种范围划分,请在频道名称前加上环境名称(测试、预发布、生产等)。
线协议示例
SUBSCRIBE first second
*3
$9
subscribe
$5
first
:1
*3
$9
subscribe
$6
second
:2
此时,我们从另一个客户端对名为 second
的频道执行 PUBLISH
操作
> PUBLISH second Hello
这是第一个客户端收到的内容
*3
$7
message
$6
second
$5
Hello
现在客户端使用不带额外参数的 UNSUBSCRIBE
命令取消订阅所有频道
UNSUBSCRIBE
*3
$11
unsubscribe
$6
second
:1
*3
$11
unsubscribe
$5
first
:0
模式匹配订阅
Valkey 的发布/订阅实现支持模式匹配。客户端可以订阅 glob 风格的模式,以接收发送到与给定模式匹配的频道名称的所有消息。
例如
PSUBSCRIBE news.*
将接收发送到频道 news.art.figurative
、news.music.jazz
等的所有消息。所有 glob 风格的模式都是有效的,因此支持多个通配符。
PUNSUBSCRIBE news.*
然后将客户端从此模式中取消订阅。此调用不会影响其他订阅。
模式匹配收到的消息以不同格式发送
- 消息类型为
pmessage
:这是另一个客户端发出的PUBLISH
命令所接收到的消息,它匹配模式匹配订阅。第二个元素是原始匹配模式,第三个元素是源频道的名称,最后一个元素是实际的消息负载。
与 SUBSCRIBE
和 UNSUBSCRIBE
类似,PSUBSCRIBE
和 PUNSUBSCRIBE
命令通过系统发送类型为 psubscribe
和 punsubscribe
的消息来确认,其格式与 subscribe
和 unsubscribe
消息格式相同。
同时匹配模式和频道订阅的消息
如果客户端订阅了与已发布消息匹配的多个模式,或者同时订阅了与消息匹配的模式和频道,则客户端可能会多次接收同一条消息。这由以下示例所示:
SUBSCRIBE foo
PSUBSCRIBE f*
在上述示例中,如果消息发送到频道 foo
,客户端将收到两条消息:一条类型为 message
,一条类型为 pmessage
。
模式匹配中订阅计数的含义
在 subscribe
、unsubscribe
、psubscribe
和 punsubscribe
消息类型中,最后一个参数是仍然活动的订阅数量。这个数字是客户端仍然订阅的频道和模式的总数。因此,客户端只有在取消订阅所有频道和模式导致此计数降至零时才会退出发布/订阅状态。
分片发布/订阅
从 Redis OSS 7.0 开始,引入了分片发布/订阅,其中分片频道通过与分配键到槽相同的算法分配到槽。分片消息必须发送到拥有分片频道哈希到的槽的节点。集群确保已发布的分片消息转发到分片中的所有节点,因此客户端可以通过连接到负责该槽的主节点或其任何副本节点来订阅分片频道。SSUBSCRIBE
、SUNSUBSCRIBE
和 SPUBLISH
用于实现分片发布/订阅。
分片发布/订阅有助于在集群模式下扩展发布/订阅的使用。它将消息的传播限制在集群的分片内部。因此,与全局发布/订阅相比,通过集群总线的数据量是有限的,全局发布/订阅中每条消息都会传播到集群中的每个节点。这允许用户通过添加更多分片来水平扩展发布/订阅的使用。
客户端库实现提示
由于所有接收到的消息都包含导致消息传递的原始订阅(消息类型是频道,pmessage 类型是原始模式),客户端库可以使用哈希表将原始订阅绑定到回调(可以是匿名函数、块、函数指针)。
当收到消息时,可以进行 O(1) 查找以将消息传递给已注册的回调。