发布与订阅
SUBSCRIBE
、UNSUBSCRIBE
和 PUBLISH
实现了发布/订阅消息范式。在该范式中(引用维基百科的说法),发送者(发布者)在编程时不会将其消息发送给特定的接收者(订阅者)。相反,发布的消息被归类到不同的频道中,发布者并不知道有哪些(或是否有)订阅者。订阅者表达对一个或多个频道的兴趣,并且只接收他们感兴趣的消息,而无需知道有哪些(或是否有)发布者。这种发布者和订阅者之间的解耦可以实现更大的可扩展性和更动态的网络拓扑。
例如,为了订阅频道 foo
和 bar
,客户端需要发出一个 SUBSCRIBE
命令,并提供频道的名称:
其他客户端发送到这些频道的消息将被 KeyDB 推送给所有订阅的客户端。
一个订阅了一个或多个频道的客户端不应该发出其他命令,尽管它可以订阅和取消订阅其他频道。对订阅和取消订阅操作的回复会以消息的形式发送,这样客户端只需读取一个连贯的消息流,其中第一个元素指明了消息的类型。在订阅客户端的上下文中,允许使用的命令有 SUBSCRIBE
、PSUBSCRIBE
、UNSUBSCRIBE
、PUNSUBSCRIBE
、PING
和 QUIT
。
请注意,keydb-cli
一旦进入订阅模式,将不接受任何命令,只能通过 Ctrl-C
退出该模式。
#
推送消息的格式一条消息是一个包含三个元素的@array-reply(数组回复)。
第一个元素是消息的类型:
subscribe
:表示我们成功订阅了回复中第二个元素指定的频道。第三个参数代表我们当前订阅的频道数量。unsubscribe
:表示我们成功地从回复中第二个元素指定的频道取消了订阅。第三个参数代表我们当前订阅的频道数量。当最后一个参数为零时,我们不再订阅任何频道,客户端可以发出任何类型的 KeyDB 命令,因为我们已经退出了发布/订阅状态。message
:这是作为另一个客户端发出的PUBLISH
命令的结果而收到的消息。第二个元素是源频道的名称,第三个参数是实际的消息内容。
#
数据库和作用域发布/订阅与键空间(key space)没有任何关系。它的设计初衷就是为了不以任何方式干扰键空间,包括数据库编号。
在数据库 10 上发布的消息,会被数据库 1 上的订阅者听到。
如果您需要某种形式的作用域,可以用环境名称(例如:test、staging、production...)作为频道的前缀。
#
线路协议示例此时,我们从另一个客户端向名为 second
的频道发出一个 PUBLISH
操作:
这是第一个客户端收到的内容:
现在,客户端使用不带参数的 UNSUBSCRIBE
命令来取消订阅所有频道:
#
模式匹配订阅KeyDB 的发布/订阅实现支持模式匹配。客户端可以订阅 glob 风格的模式,以接收所有发送到与给定模式匹配的频道名称的消息。
例如:
将会收到所有发送到频道 news.art.figurative
、news.music.jazz
等的消息。所有 glob 风格的模式都是有效的,因此支持多个通配符。
此命令将取消客户端对该模式的订阅。其他订阅不会受此调用影响。
通过模式匹配收到的消息会以不同的格式发送:
- 消息的类型是
pmessage
:这是由于另一个客户端发出PUBLISH
命令,且匹配了一个模式匹配订阅而收到的消息。第二个元素是匹配到的原始模式,第三个元素是源频道的名称,最后一个元素是实际的消息内容。
与 SUBSCRIBE
和 UNSUBSCRIBE
类似,PSUBSCRIBE
和 PUNSUBSCRIBE
命令会通过系统发送类型为 psubscribe
和 punsubscribe
的消息来确认,其格式与 subscribe
和 unsubscribe
消息格式相同。
#
同时匹配模式和频道订阅的消息如果一个客户端订阅了多个与已发布消息相匹配的模式,或者同时订阅了与消息相匹配的模式和频道,那么它可能会多次收到同一条消息。如下例所示:
在上面的例子中,如果一条消息被发送到频道 foo
,客户端将会收到两条消息:一条类型为 message
,另一条类型为 pmessage
。
#
模式匹配下订阅计数的含义在 subscribe
、unsubscribe
、psubscribe
和 punsubscribe
消息类型中,最后一个参数是仍然活跃的订阅数量。这个数字实际上是客户端仍然订阅的频道和模式的总数。因此,只有当这个计数因为取消订阅了所有的频道和模式而降至零时,客户端才会退出发布/订阅状态。
#
编程示例Pieter Noordhuis 提供了一个很好的示例,他使用 EventMachine 和 Redis 创建了一个多用户高性能的网页聊天室。
#
客户端库实现提示因为所有收到的消息都包含了导致消息传递的原始订阅(对于 message 类型是频道,对于 pmessage 类型是原始模式),所以客户端库可以使用哈希表将原始订阅与回调函数(可以是匿名函数、块、函数指针)绑定起来。
当收到消息时,可以进行一次 O(1) 的查找,以便将消息传递给注册的回调函数。