发布订阅(pub/sub)是一种消息通信模式,主要的目的是解耦消息发布者和消息订阅者之间的耦合,这点和设计模式中的观察者模式比较相似。pub/sub 不仅仅解决发布者和订阅者直接代码级别耦合也解决两者在物理部署上的耦合。redis 作为一个 pub/sub 的 server,在订阅者和发布者之间起到了消息路由的功能。订阅者可以通过 subscribe 和 psubscribe 命令向 redisserver 订阅自己感兴趣的消息类型,redis 将消息类型称为通道(channel)。当发布者通过publish 命令向 redis server 发送特定类型的消息时。订阅该消息类型的全部 client 都会收到此消息。这里消息的传递是多对多的。一个 client 可以订阅多个 channel,也可以向多个 channel发送消息。
频道转发的每条信息都是一条带有三个元素的多条批量回复(multi-bulk reply)。
-unsubscribe : 表示当前客户端成功地退订了信息第二个元素所指示的频道。 信息的第三个元素记录了客户端目前仍在订阅的频道数量。 当客户端订阅的频道数量降为 0 时, 客户端不再订阅任何频道, 它可以像往常一样, 执行任何 Redis 命令。
message : 表示这条信息是由某个客户端执行 PUBLISH 命令所发送的, 真正的信息。 信息的第二个元素是信息来源的频道, 而第三个元素则是信息的内容。
举个例子, 如果客户端执行以下命令:
如果在这时, 另一个客户端执行以下 PUBLISH 命令:
redis> PUBLISH second Hello
那么之前订阅了 second 频道的客户端将收到以下信息:
1) "message"
2) "second"
3) "hello"
当订阅者决定退订所有频道时, 它可以执行一个无参数的 UNSUBSCRIBE 命令:
订阅模式:
Redis 的发布与订阅实现支持模式匹配(pattern matching): 客户端可以订阅一个带 * 号的模式, 如果某个/某些频道的名字和这个模式匹配, 那么当有信息发送给这个/这些频道的时候, 客户端也会收到这个/这些频道的信息。
通过订阅模式接收到的信息, 和通过订阅频道接收到的信息, 这两者的格式不太一样:
通过订阅模式而接收到的信息的类型为pmessage:这代表有某个客户端通过PUBLISH向某个频道发送了信息,而这个频道刚好匹配了当前客户端所订阅的某个模式。 信息的第二个元素记录了被匹配的模式, 第三个元素记录了被匹配的频道的名字, 最后一个元素则记录了信息的实际内容。
Redis命令参考>>PUBSUB命令
频道转发的每条信息都是一条带有三个元素的多条批量回复(multi-bulk reply)。
信息的第一个元素标识了信息的类型:
-subscribe : 表示当前客户端成功地订阅了信息第二个元素所指示的频道。 而信息的第三个元素则记录了目前客户端已订阅频道的总数。-unsubscribe : 表示当前客户端成功地退订了信息第二个元素所指示的频道。 信息的第三个元素记录了客户端目前仍在订阅的频道数量。 当客户端订阅的频道数量降为 0 时, 客户端不再订阅任何频道, 它可以像往常一样, 执行任何 Redis 命令。
message : 表示这条信息是由某个客户端执行 PUBLISH 命令所发送的, 真正的信息。 信息的第二个元素是信息来源的频道, 而第三个元素则是信息的内容。
举个例子, 如果客户端执行以下命令:
redis> SUBSCRIBE first second
那么它将收到以下回复:
1) "subscribe"
2) "first"
3) (integer) 1
1) "subscribe"
2) "second"
3) (integer) 2
如果在这时, 另一个客户端执行以下 PUBLISH 命令:
redis> PUBLISH second Hello
那么之前订阅了 second 频道的客户端将收到以下信息:
1) "message"
2) "second"
3) "hello"
当订阅者决定退订所有频道时, 它可以执行一个无参数的 UNSUBSCRIBE 命令:
redis> UNSUBSCRIBE
这个命令将接到以下回复:
1) "unsubscribe"
2) "second"
3) (integer) 1
1) "unsubscribe"
2) "first"
3) (integer) 0
订阅模式:
Redis 的发布与订阅实现支持模式匹配(pattern matching): 客户端可以订阅一个带 * 号的模式, 如果某个/某些频道的名字和这个模式匹配, 那么当有信息发送给这个/这些频道的时候, 客户端也会收到这个/这些频道的信息。
通过订阅模式接收到的信息, 和通过订阅频道接收到的信息, 这两者的格式不太一样:
通过订阅模式而接收到的信息的类型为pmessage:这代表有某个客户端通过PUBLISH向某个频道发送了信息,而这个频道刚好匹配了当前客户端所订阅的某个模式。 信息的第二个元素记录了被匹配的模式, 第三个元素记录了被匹配的频道的名字, 最后一个元素则记录了信息的实际内容。
客户端处理 PSUBSCRIBE 和 PUNSUBSCRIBE 返回值的方式, 和客户端处理 SUBSCRIBE 和 UNSUBSCRIBE 的方式类似: 通过对信息的第一个元素进行分析, 客户端可以判断接收到的信息是一个真正的信息, 还是 PSUBSCRIBE 或 PUNSUBSCRIBE 命令的返回值。
内部实现:
struct redisServer {
...
// 这个字典记录了客户端所有订阅的频道
// 键为频道名字,值为 NULL
// 也即是,一个频道的集合
dict *pubsub_channels; /* channels a client is interested in (SUBSCRIBE) */
// 链表,包含多个 pubsubPattern 结构
// 记录了所有订阅频道的客户端的信息
// 新 pubsubPattern 结构总是被添加到表尾
list *pubsub_patterns; /* patterns a client is interested in (SUBSCRIBE) */
}
/*
* 记录订阅模式的结构
*/
typedef struct pubsubPattern {
// 订阅模式的客户端
redisClient *client;
// 被订阅的模式
robj *pattern;
} pubsubPattern;
Redis命令参考>>PUBSUB命令