“Redis:发布/订阅(pub/sub)”的版本间差异
跳到导航
跳到搜索
无编辑摘要 |
小 (Eijux移动页面Redis:发布/订阅至Redis:发布/订阅(pub/sub),不留重定向) |
||
(未显示同一用户的15个中间版本) | |||
第3行: | 第3行: | ||
__TOC__ | __TOC__ | ||
== | == 关于 == | ||
SUBSCRIBE 、 UNSUBSCRIBE 和 PUBLISH 三个命令实现了发布与订阅信息泛型(Publish/Subscribe messaging paradigm),在这个实现中: | |||
1、发送者(发送信息的客户端)不是将信息直接发送给特定的接收者(接收信息的客户端),而是将信息发送给频道(channel),然后由频道将信息转发给所有对这个频道感兴趣的订阅者。 | |||
2、发送者无须知道任何关于订阅者的信息,而订阅者也无须知道是那个客户端给它发送信息,它只要关注自己感兴趣的频道即可。 | |||
对发布者和订阅者进行解构(decoupling), 可以极大地提高系统的扩展性(scalability), 并得到一个更动态的网络拓扑(network topology)。 | |||
发布订阅(pub/sub)是一种消息通信模式:发送者(publish)发送消息,订阅者(subscribe)接收消息。 | |||
:[[File:redis发布.png|left|thumb|200px|发布]] [[File:reids订阅.png|none|thumb|300px|订阅]] | |||
=== “发布订阅”与“消息队列” === | |||
发布订阅也叫“'''生产者消费者'''”模式,是实现“'''消息队列'''”的一种方式。基于消息队列,可以实现系统解耦、削峰填谷,顶住流量洪峰。 | |||
* redis 的主业目前是基于键值对的数据存储、缓存等,消息队列可能是 redis 的一种尝试; | |||
* 常用的流行的消息队列有:“'''ActiveMQ'''”、“'''RabbitMQ'''”等; | |||
第13行: | 第25行: | ||
# 消息服务(broker) | # 消息服务(broker) | ||
== Pub/Sub 命令 == | |||
正在订阅频道的客户端不应该发送除下述之外的其他命令: | |||
{| class="wikitable" | |||
|+ Redis:发布/订阅 命令 | |||
! !! 命令 !! 描述 | |||
|- | |||
| rowspan="2" | 订阅频道 | |||
| SUBSCRIBE <channel> [<channel> ...] || '''订阅'''给定的一个或多个频道的信息。 | |||
* 返回值:“信息”(参见下节“信息的格式”); | |||
|- | |||
| UNSUBSCRIBE [<channel> [<channel> ...]] || '''退订'''给定的频道。 | |||
* 返回值在不同的客户端中有不同的表现; | |||
|- | |||
| rowspan="2" | 订阅模式 | |||
| PSUBSCRIBE <pattern> [<pattern> ...] || '''订阅'''一个或多个符合给定模式的频道。 | |||
* 返回值:“信息”; | |||
|- | |||
| PUNSUBSCRIBE [<pattern> [<pattern> ...]] || '''退订'''给定模式的频道。 | |||
* 返回值在不同的客户端中有不同的表现; | |||
|- | |||
| 发布 | |||
| PUBLISH <channel> <message> || 将信息'''发布'''到指定的频道。 | |||
* 返回值:接收到信息 <message> 的订阅者数量。 | |||
|- | |||
| 系统状态 | |||
| PUBSUB <subcommand> [<argument> [<argument> ...]] || 它是一个查看订阅与发布系统状态的内省命令,由数个不同格式的子命令组成。 | |||
|} | |||
* 客户端订阅的模式里面可以包含多个 glob 风格的通配符, 比如: '''*''' 、 '''?''' 和 '''[...]''',等等。 | |||
* 在执行 SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE 和 PUNSUBSCRIBE 命令时,返回结果的最后一个元素是客户端目前'''仍在订阅的频道和模式总数'''。 | |||
** 当客户端退订所有频道和模式,也即是这个总数值下降为 0 的时候,客户端将退出订阅与发布状态。 | |||
=== PUBSUB === | |||
PUBSUB 有多个可用的子命令: | |||
# <syntaxhighlight lang="bash" highlight=""> | |||
# 列出当前的活跃频道: | |||
PUBSUB CHANNELS [pattern] | |||
# 返回值:一个由活跃频道组成的列表。 | |||
</syntaxhighlight> | |||
#* 活跃频道:指的是那些至少有一个订阅者的频道(订阅模式的客户端不计算在内)。 | |||
#* 如果给出 pattern 参数,那么只列出和 pattern 相匹配的活跃频道,否则列出订阅与发布系统中的所有活跃频道。 | |||
#* 即使一个频道有多个订阅者,它也只输出一次。 | |||
#: 示例: | |||
#: <syntaxhighlight lang="bash" highlight=""> | |||
# client-1 订阅 news.it 和 news.sport 两个频道 | |||
# client-2 订阅 news.it 和 news.internet 两个频道 | |||
# 使用 client-3 打印所有活跃频道: | |||
redis> PUBSUB CHANNELS | |||
1) "news.sport" | |||
2) "news.internet" | |||
3) "news.it" | |||
# 打印匹配的活跃频道: | |||
redis> PUBSUB CHANNELS news.i* | |||
1) "news.internet" | |||
2) "news.it" | |||
</syntaxhighlight> | |||
# <syntaxhighlight lang="bash" highlight=""> | |||
# 返回给定频道的订阅者数量(订阅模式的客户端不计算在内): | |||
PUBSUB NUMSUB [channel-1 ... channel-N] | |||
# 返回值:一个多条批量回复(Multi-bulk reply),回复中包含给定的频道,以及频道的订阅者数量。 | |||
# 格式为“频道channel-1,channel-1的订阅者数量,频道channel-2,channel-2的订阅者数量 . . .”。 | |||
</syntaxhighlight> | |||
#* 回复中频道的排列顺序和执行命令时给定频道的排列顺序一致。 | |||
#* 不给定任何频道而直接调用这个命令也是可以的,在这种情况下,命令只返回一个空列表。 | |||
#: 示例: | |||
#: <syntaxhighlight lang="bash" highlight=""> | |||
# client-1 订阅 news.it 和 news.sport 两个频道 | |||
# client-2 订阅 news.it 和 news.internet 两个频道 | |||
# 使用 client-3 打印各个频道的订阅者数量: | |||
redis> PUBSUB NUMSUB news.it news.internet news.sport news.music | |||
1) "news.it" # 频道 | |||
2) "2" # 订阅该频道的客户端数量 | |||
3) "news.internet" | |||
4) "1" | |||
5) "news.sport" | |||
6) "1" | |||
7) "news.music" # 没有任何订阅者 | |||
8) "0" | |||
</syntaxhighlight> | |||
# <syntaxhighlight lang="bash" highlight=""> | |||
# 返回订阅模式的数量: | |||
PUBSUB NUMPAT | |||
# 返回值:一个整数回复(Integer reply)。 | |||
: | </syntaxhighlight> | ||
#* 这个命令返回的不是订阅模式的客户端的数量,而是“所有客户端订阅的模式的数量总和”(多个客户端订阅相同的模式时,会被重复计算)。 | |||
#: 示例: | |||
#: <syntaxhighlight lang="bash" highlight=""> | |||
# client-1 订阅 news.* 和 discount.* 两个模式 | |||
# client-2 订阅 tweet.* 一个模式 | |||
== | # 使用 client-3 打印当前订阅模式的数量: | ||
redis> PUBSUB NUMPAT | |||
(integer) 3 | |||
# 再新建一个客户端 client-4 ,让它也订阅 news.* 频道 | |||
# 再使用 client-3 打印当前订阅模式的数量: | |||
redis> PUBSUB NUMPAT | |||
(integer) 4 | |||
</syntaxhighlight> | |||
== 频道和模式 == | |||
=== 订阅频道 === | |||
<syntaxhighlight lang="java" highlight=""> | |||
// 订阅 | |||
subscribe <channel> [<channel> ...] | |||
// 发布 | |||
publish <channel> <message> | |||
</syntaxhighlight> | |||
(以命令行模拟实现): | (以命令行模拟实现): | ||
# 开启 4 个 redis 客户端(“./redis-cli”),如上图,3 个客户端作为消息订阅者,1 个为消息发布者: | # 开启 4 个 redis 客户端(“./redis-cli”),如上图,3 个客户端作为消息订阅者,1 个为消息发布者: | ||
# 让 3 个消息订阅者订阅某个频道主题:“'''<code>subscribe channelTest</code>'''”; | # 让 3 个消息订阅者订阅某个频道主题:“'''<code>subscribe channelTest</code>'''”; | ||
#: [[File:redis订阅channelTest.jpg|800px]] | #: [[File:redis订阅channelTest.jpg|800px]] | ||
# | # 让 1 个消息发布者向频道主题上发布消息:“'''<code>publish channelTest message123</code>'''”; | ||
#: [[File:redis发布消息到channelTest.jpg|800px]] | #: [[File:redis发布消息到channelTest.jpg|800px]] | ||
=== 订阅模式 === | |||
<syntaxhighlight lang="java" highlight=""> | |||
// 订阅 | |||
psubscribe <pattern> [<pattern> ...] | |||
// 发布 | |||
publish <channel> <message> | |||
</syntaxhighlight> | |||
(以命令行模拟实现): | |||
# 订阅匹配模式的频道:“'''<code>psubscribe chan*</code>'''” | |||
#:[[File:redis订阅(psubscribe).jpg|800px]] | #:[[File:redis订阅(psubscribe).jpg|800px]] | ||
# 发布: | # 发布: | ||
#:[[File:redis发布(psubscribe).jpg|800px]] | #:[[File:redis发布(psubscribe).jpg|800px]] | ||
== 信息的格式 == | |||
频道转发的每条信息都是一条带有三个元素的多条批量回复(multi-bulk reply)。 | |||
信息类型:(信息的第一个元素) | |||
# “'''subscribe'''”:表示当前客户端成功地订阅了(信息第二个元素所指示的)频道。 | |||
#: 第二个元素:订阅的频道; | |||
#: 第三个元素:客户端目前已订阅的频道数量。 | |||
# “'''unsubscribe'''”:表示当前客户端成功地退订了(信息第二个元素所指示的)频道。 | |||
#: 第二个元素:退订的频道; | |||
#: 第三个元素:客户端目前仍在订阅的频道数量。 | |||
#* 当客户端订阅的频道数量降为 0 时,客户端不再订阅任何频道,它可以像往常一样,执行任何 Redis 命令。 | |||
# “'''message'''”:表示这条信息是由某个客户端执行 PUBLISH 命令所发送的,真正的信息。 | |||
#: 第二个元素:信息来源的频道; | |||
#: 第三个元素:信息的内容。 | |||
# “'''pmessage '''”:表示有某个客户端通过 PUBLISH 向某个频道发送了信息,而这个频道刚好匹配了当前客户端所订阅的某个模式。 | |||
#: 第二个元素:被匹配的模式; | |||
#: 第三个元素:被匹配的频道的名字; | |||
#: 最后一个元素:信息的实际内容。 | |||
示例: | |||
# subscribe 类型的信息: | |||
#: <syntaxhighlight lang="bash" highlight=""> | |||
redis> SUBSCRIBE first second | |||
1) "subscribe" | |||
2) "first" | |||
3) (integer) 1 | |||
1) "subscribe" | |||
2) "second" | |||
3) (integer) 2 | |||
</syntaxhighlight> | |||
# message 类型的信息: | |||
#: 客户端B订阅频道、客户端A项频道发送数据后,客户端B所接受到信息 | |||
#: <syntaxhighlight lang="bash" highlight=""> | |||
redis> SUBSCRIBE hello | |||
1) "subscribe" | |||
2) "hello" | |||
3) (integer) 1 | |||
1) "message" | |||
2) "second" | |||
3) "hello" | |||
</syntaxhighlight> | |||
# unsubscribe 类型的信息: | |||
#: <syntaxhighlight lang="bash" highlight=""> | |||
redis> UNSUBSCRIBE | |||
1) "unsubscribe" | |||
2) "second" | |||
3) (integer) 1 | |||
1) "unsubscribe" | |||
2) "first" | |||
3) (integer) 0 | |||
</syntaxhighlight> | |||
== FAQ == | |||
=== 通过频道和模式接收同一条信息 === | |||
如果客户端订阅的多个模式匹配了同一个频道,或者客户端同时订阅了某个频道、以及匹配这个频道的某个模式,那么它可能会多次接收到同一条信息。 | |||
示例: | |||
: 如果客户端执行了以下命令: | |||
: <syntaxhighlight lang="bash" highlight=""> | |||
SUBSCRIBE foo | |||
PSUBSCRIBE f* | |||
</syntaxhighlight> | |||
: 当有信息发送到频道 foo 时,客户端将收到两条信息:一条来自频道 foo,信息类型为 message;另一条来自模式 f*,信息类型为 pmessage 。 |
2021年11月3日 (三) 01:10的最新版本
关于
SUBSCRIBE 、 UNSUBSCRIBE 和 PUBLISH 三个命令实现了发布与订阅信息泛型(Publish/Subscribe messaging paradigm),在这个实现中: 1、发送者(发送信息的客户端)不是将信息直接发送给特定的接收者(接收信息的客户端),而是将信息发送给频道(channel),然后由频道将信息转发给所有对这个频道感兴趣的订阅者。 2、发送者无须知道任何关于订阅者的信息,而订阅者也无须知道是那个客户端给它发送信息,它只要关注自己感兴趣的频道即可。 对发布者和订阅者进行解构(decoupling), 可以极大地提高系统的扩展性(scalability), 并得到一个更动态的网络拓扑(network topology)。
发布订阅(pub/sub)是一种消息通信模式:发送者(publish)发送消息,订阅者(subscribe)接收消息。
“发布订阅”与“消息队列”
发布订阅也叫“生产者消费者”模式,是实现“消息队列”的一种方式。基于消息队列,可以实现系统解耦、削峰填谷,顶住流量洪峰。
- redis 的主业目前是基于键值对的数据存储、缓存等,消息队列可能是 redis 的一种尝试;
- 常用的流行的消息队列有:“ActiveMQ”、“RabbitMQ”等;
消息队列的三要素:
- 生产者(producer)
- 消费者(consumer)
- 消息服务(broker)
Pub/Sub 命令
正在订阅频道的客户端不应该发送除下述之外的其他命令:
命令 | 描述 | |
---|---|---|
订阅频道 | SUBSCRIBE <channel> [<channel> ...] | 订阅给定的一个或多个频道的信息。
|
UNSUBSCRIBE [<channel> [<channel> ...]] | 退订给定的频道。
| |
订阅模式 | PSUBSCRIBE <pattern> [<pattern> ...] | 订阅一个或多个符合给定模式的频道。
|
PUNSUBSCRIBE [<pattern> [<pattern> ...]] | 退订给定模式的频道。
| |
发布 | PUBLISH <channel> <message> | 将信息发布到指定的频道。
|
系统状态 | PUBSUB <subcommand> [<argument> [<argument> ...]] | 它是一个查看订阅与发布系统状态的内省命令,由数个不同格式的子命令组成。 |
- 客户端订阅的模式里面可以包含多个 glob 风格的通配符, 比如: * 、 ? 和 [...],等等。
- 在执行 SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE 和 PUNSUBSCRIBE 命令时,返回结果的最后一个元素是客户端目前仍在订阅的频道和模式总数。
- 当客户端退订所有频道和模式,也即是这个总数值下降为 0 的时候,客户端将退出订阅与发布状态。
PUBSUB
PUBSUB 有多个可用的子命令:
# 列出当前的活跃频道: PUBSUB CHANNELS [pattern] # 返回值:一个由活跃频道组成的列表。
- 活跃频道:指的是那些至少有一个订阅者的频道(订阅模式的客户端不计算在内)。
- 如果给出 pattern 参数,那么只列出和 pattern 相匹配的活跃频道,否则列出订阅与发布系统中的所有活跃频道。
- 即使一个频道有多个订阅者,它也只输出一次。
- 示例:
# client-1 订阅 news.it 和 news.sport 两个频道 # client-2 订阅 news.it 和 news.internet 两个频道 # 使用 client-3 打印所有活跃频道: redis> PUBSUB CHANNELS 1) "news.sport" 2) "news.internet" 3) "news.it" # 打印匹配的活跃频道: redis> PUBSUB CHANNELS news.i* 1) "news.internet" 2) "news.it"
# 返回给定频道的订阅者数量(订阅模式的客户端不计算在内): PUBSUB NUMSUB [channel-1 ... channel-N] # 返回值:一个多条批量回复(Multi-bulk reply),回复中包含给定的频道,以及频道的订阅者数量。 # 格式为“频道channel-1,channel-1的订阅者数量,频道channel-2,channel-2的订阅者数量 . . .”。
- 回复中频道的排列顺序和执行命令时给定频道的排列顺序一致。
- 不给定任何频道而直接调用这个命令也是可以的,在这种情况下,命令只返回一个空列表。
- 示例:
# client-1 订阅 news.it 和 news.sport 两个频道 # client-2 订阅 news.it 和 news.internet 两个频道 # 使用 client-3 打印各个频道的订阅者数量: redis> PUBSUB NUMSUB news.it news.internet news.sport news.music 1) "news.it" # 频道 2) "2" # 订阅该频道的客户端数量 3) "news.internet" 4) "1" 5) "news.sport" 6) "1" 7) "news.music" # 没有任何订阅者 8) "0"
# 返回订阅模式的数量: PUBSUB NUMPAT # 返回值:一个整数回复(Integer reply)。
- 这个命令返回的不是订阅模式的客户端的数量,而是“所有客户端订阅的模式的数量总和”(多个客户端订阅相同的模式时,会被重复计算)。
- 示例:
# client-1 订阅 news.* 和 discount.* 两个模式 # client-2 订阅 tweet.* 一个模式 # 使用 client-3 打印当前订阅模式的数量: redis> PUBSUB NUMPAT (integer) 3 # 再新建一个客户端 client-4 ,让它也订阅 news.* 频道 # 再使用 client-3 打印当前订阅模式的数量: redis> PUBSUB NUMPAT (integer) 4
频道和模式
订阅频道
// 订阅
subscribe <channel> [<channel> ...]
// 发布
publish <channel> <message>
(以命令行模拟实现):
- 开启 4 个 redis 客户端(“./redis-cli”),如上图,3 个客户端作为消息订阅者,1 个为消息发布者:
- 让 3 个消息订阅者订阅某个频道主题:“
subscribe channelTest
”; - 让 1 个消息发布者向频道主题上发布消息:“
publish channelTest message123
”;
订阅模式
// 订阅
psubscribe <pattern> [<pattern> ...]
// 发布
publish <channel> <message>
(以命令行模拟实现):
信息的格式
频道转发的每条信息都是一条带有三个元素的多条批量回复(multi-bulk reply)。
信息类型:(信息的第一个元素)
- “subscribe”:表示当前客户端成功地订阅了(信息第二个元素所指示的)频道。
- 第二个元素:订阅的频道;
- 第三个元素:客户端目前已订阅的频道数量。
- “unsubscribe”:表示当前客户端成功地退订了(信息第二个元素所指示的)频道。
- 第二个元素:退订的频道;
- 第三个元素:客户端目前仍在订阅的频道数量。
- 当客户端订阅的频道数量降为 0 时,客户端不再订阅任何频道,它可以像往常一样,执行任何 Redis 命令。
- “message”:表示这条信息是由某个客户端执行 PUBLISH 命令所发送的,真正的信息。
- 第二个元素:信息来源的频道;
- 第三个元素:信息的内容。
- “pmessage ”:表示有某个客户端通过 PUBLISH 向某个频道发送了信息,而这个频道刚好匹配了当前客户端所订阅的某个模式。
- 第二个元素:被匹配的模式;
- 第三个元素:被匹配的频道的名字;
- 最后一个元素:信息的实际内容。
示例:
- subscribe 类型的信息:
redis> SUBSCRIBE first second 1) "subscribe" 2) "first" 3) (integer) 1 1) "subscribe" 2) "second" 3) (integer) 2
- message 类型的信息:
- 客户端B订阅频道、客户端A项频道发送数据后,客户端B所接受到信息
redis> SUBSCRIBE hello 1) "subscribe" 2) "hello" 3) (integer) 1 1) "message" 2) "second" 3) "hello"
- unsubscribe 类型的信息:
redis> UNSUBSCRIBE 1) "unsubscribe" 2) "second" 3) (integer) 1 1) "unsubscribe" 2) "first" 3) (integer) 0
FAQ
通过频道和模式接收同一条信息
如果客户端订阅的多个模式匹配了同一个频道,或者客户端同时订阅了某个频道、以及匹配这个频道的某个模式,那么它可能会多次接收到同一条信息。
示例:
- 如果客户端执行了以下命令:
SUBSCRIBE foo PSUBSCRIBE f*
- 当有信息发送到频道 foo 时,客户端将收到两条信息:一条来自频道 foo,信息类型为 message;另一条来自模式 f*,信息类型为 pmessage 。