Redis 发布/订阅
如何在 Redis 中使用发布/订阅频道
SUBSCRIBE
、UNSUBSCRIBE
和 PUBLISH
实现 发布/订阅消息传递范式,其中(引用维基百科)发送者(发布者)不会被编程为将消息发送到特定的接收者(订阅者)。相反,发布的消息被分类到频道中,而发布者不知道可能存在哪些(如果有的话)订阅者。订阅者对一个或多个频道表示兴趣,并且只接收他们感兴趣的消息,而不知道可能存在哪些(如果有的话)发布者。发布者和订阅者的这种解耦使网络拓扑结构能够实现更高的可扩展性和动态性。
例如,要订阅“channel11”和“ch:00”频道,客户端发出 SUBSCRIBE
命令,提供这些频道的名称。
SUBSCRIBE channel11 ch:00
其他客户端发送到这些频道的消息将由 Redis 推送到所有订阅的客户端。订阅者按消息发布的顺序接收消息。
订阅了一个或多个频道的客户端不应该发出命令,尽管它可以 SUBSCRIBE
和 UNSUBSCRIBE
到其他频道。订阅和取消订阅操作的回复以消息的形式发送,以便客户端只需读取一个连贯的消息流,其中第一个元素指示消息类型。在订阅的 RESP2 客户端上下文中允许的命令是
但是,如果使用 RESP3(参见 HELLO
),客户端可以在订阅状态下发出任何命令。
请注意,当使用 redis-cli
时,在订阅模式下,诸如 UNSUBSCRIBE
和 PUNSUBSCRIBE
之类的命令不能使用,因为 redis-cli
不会接受任何命令,只能使用 Ctrl-C
退出模式。
传递语义
Redis 的发布/订阅表现出 *至多一次* 消息传递语义。顾名思义,这意味着一条消息最多只会被传递一次。消息一旦被 Redis 服务器发送,就没有再次发送的可能性。如果订阅者无法处理消息(例如,由于错误或网络断开连接),消息将永远丢失。
如果您的应用程序需要更强的传递保证,您可能需要了解 Redis 流。流中的消息是持久的,并且支持 *至多一次* 和 *至少一次* 传递语义。
推送消息的格式
消息是一个带有三个元素的 数组回复。
第一个元素是消息类型
-
subscribe
:表示我们已成功订阅了回复中作为第二个元素给出的频道。第三个参数表示我们当前订阅的频道数量。 -
unsubscribe
:表示我们已成功取消订阅了回复中作为第二个元素给出的频道。第三个参数表示我们当前订阅的频道数量。当最后一个参数为零时,我们不再订阅任何频道,并且客户端可以发出任何类型的 Redis 命令,因为我们已处于发布/订阅状态之外。 -
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
模式匹配订阅
Redis 发布/订阅实现支持模式匹配。客户端可以订阅 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 7.0 开始,引入了分片发布/订阅,其中分片频道通过与将键分配到槽位相同的算法分配到槽位。分片消息必须发送到拥有分片频道被哈希到的槽位的节点。集群确保发布的分片消息被转发到分片中的所有节点,因此客户端可以通过连接到负责该槽位的 master 或其任何副本来订阅分片频道。 SSUBSCRIBE
、SUNSUBSCRIBE
和 SPUBLISH
用于实现分片发布/订阅。
分片发布/订阅有助于在集群模式下扩展发布/订阅的使用。它限制了消息的传播范围,使其仅限于集群的分片。因此,与每个消息都传播到集群中每个节点的全局发布/订阅相比,通过集群总线传递的数据量有限。这允许用户通过添加更多分片来水平扩展发布/订阅的使用。
编程示例
Pieter Noordhuis 提供了一个使用 EventMachine 和 Redis 创建 多用户高性能网络聊天 的绝佳示例。
客户端库实现提示
由于接收到的所有消息都包含导致消息传递的原始订阅(消息类型情况下的频道,以及 pmessage 类型情况下的原始模式),客户端库可以使用哈希表将原始订阅绑定到回调(可以是匿名函数、块、函数指针)。
收到消息后,可以执行 O(1) 查找以将消息传递到已注册的回调。