redis发布订阅

前言

  Redis的发布与订阅模型在许多编程语言中都有实现,也就是我们常说的设计模式中的一种——观察者模式。在一些应用场合,例如发送方不是以固定频率发送消息,如果接收方频繁去资讯发布方,这种操作无疑是很麻烦并且不友好的。

  而订阅发布模型,订阅者只需要订阅注册某个频道就好了,当有消息发送过来的时候,会通过订阅的频道接收。优势在于把耦合点独立分离处理,作为发布方和接收方的中介,实现了发布方和接收方的分离。

  订阅与发布系统是Redis的一个高级属性,多个客户端可以同时订阅同一个频道,类似广播的机制。所不同的是,一个客户端可以同时订阅多个频道。也就是Redis客户端可以订阅任意数量的频道。

  下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

  当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

 

 实现原理

  服务器中维护着一个pubsub_channels字典,所有的频道和订阅关系都存在这里。字典的键为频道的名称,而值为订阅频道的客户端链表。

  1. 当有新的客户端订阅某个频道时,会发生一下两种情况中的一种:
    • 如果频道已经存在,则新的客户端会添加到pubsub_channels对应频道的链表末尾;
    • 如果频道原本不存在,则会为频道创建一个键,该客户端成为链表的第一个元素。

  2. 当客户端退订一个频道的时候,pubsub_channels对应键的链表会删除该客户端;

  3. 发送消息时,服务器会遍历pubsub_channels中对应键的链表,向每个客户端发送消息。

  服务器还维护着一个pubsub_patterns链表,链表的pattern属性记录了被订阅的模式,而Client属性记录了订阅模式的客户端。

  1. 当有新的客户端订阅某个模式时,进行如下步骤:
    1. 创建一个链表节点,pattern属性记录订阅的模式,Client记录订阅模式的客户端;
    2. 将这个链表节点添加到pubsub_patterns链表中。
  2. 当一个客户端退订某个模式时,服务器遍历pubsub_patterns链表,找到对应的pattern同时也是对应的Client客户端节点,将该节点删除;
  3. 发送信息时,服务器遍历pubsub_channels,查找与channels频道相匹配的模式,将消息发送给订阅了这些模式的客户端。

Redis订阅系统的优势:

  1. 当一个客户端向频道发送一个信息,订阅了同一频道/模式的多个客户端可以同时接收到信息,类似广播的机制;
  2. 便于Sentinel哨兵与服务器之间的通信并进行监控。

Redis发布订阅命令

1. psubscribe:订阅一个或多个符合给定模式的频道。

每个模式以 * 作为匹配符,比如:it* 匹配所有以 it 开头的频道(如,it.news,it.blog,it.tweets等)。news.*匹配所有以news.开头的频道。

支持的模式(patterns)有:

h?llo subscribes to hello, hallo and hxllo
h*llo subscribes to hllo and heeeello
h[ae]llo subscribes to hello and hallo, but not hillo

如果想输入普通的字符,可以在前面添加  

语法:

PSUBSCRIBE pattern [pattern ...]

实例:

 
127.0.0.1:6379> psubscribe mychannel h?llo h*llo h[ae]ll0
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "mychannel"
3) (integer) 1
1) "psubscribe"
2) "h?llo"
3) (integer) 2
1) "psubscribe"
2) "h*llo"
3) (integer) 3
1) "psubscribe"
2) "h[ae]ll0"
3) (integer) 4

2. publish:将信息发送到指定的频道

语法:

PUBLISH channel message  

返回值:接收到信息的订阅者数量

实例:

127.0.0.1:6379> publish mychannel 'mychannel message'
(integer) 1
127.0.0.1:6379> publish hello 'hello message'
(integer) 2
127.0.0.1:6379> publish hell0 'hell0 message'

3. pubsub:查看订阅与发布系统的状态,它由数个不同格式的子命令组成

语法:

PUBSUB <subcommand> [argument [argument ...]]  

pubsub channels [pattern]:列出当前活跃的频道列表,活跃是指信道含有一个或多个订阅者(不包括从模式接收订阅的客户端),如果pattern未提供,所有的信道都会被列出,否则值列出匹配上指定全局-类型模式的信道被列出。

返回值:活跃的频道列表,或者符合指定模式的频道。

实例:

127.0.0.1:6379> pubsub channels mychannel
(empty array)

PUBSUB NUMSUB [channel-1 ... channel-N]: 列出指定频道的订阅者个数(不包括订阅模式的客户端订阅者)。

返回值:频道的列表和每个列表中订阅者的个数,格式为:频道,个数,频道,个数,...简单的列表。

注意,不指定任何频道而直接调用这个命令也是可以的,此时,命令只返回一个空列表

实例:

127.0.0.1:6379> pubsub numsub
(empty array)
127.0.0.1:6379> pubsub numsub mychannel
1) "mychannel"
2) (integer) 0

pubsub numpat:返回订阅模式的数量(使用命令psubscribe命名实现)。注意,这个命令返回的不是订阅模式的客户端的数量,而是客户端订阅的所有模式的数量总和。

返回值:客户端订阅的所有模式的总和。

实例:

127.0.0.1:6379> pubsub numpat
(integer) 4

4. punsubscribe [ pattern [pattern ...]]:退订所有给定模式的频道。

说明:

该命令指示客户端退订指定模式,如果没有指定模式则退出所有的模式。

如果没有模式被指定,即一个无参的punsubscribe调用被执行,那么客户端使用psubscribe命令订阅的所有模式都会被退订。在这种情况下,命令会返回一个信息,告知客户端所有被退订的模式。

返回值:这个命令在不同的客户端中有不同的表现。

实例:

127.0.0.1:6379> punsubscribe mychannel
1) "punsubscribe"
2) "mychannel"
3) (integer) 0
127.0.0.1:6379> punsubscribe
1) "punsubscribe"
2) (nil)
3) (integer) 0
127.0.0.1:6379> 

5. subscribe:订阅给定的一个或多个频道信息

一旦客户端进入订阅状态,客户端就只可以接受订阅相关的命令SUBSCRIBEPSUBSCRIBEUNSUBSCRIBEPUNSUBSCRIBE除了这些命令,其他命令一律失效。

语法:

SUBSCRIBE channel [channel ...]  

返回值:接收到的信息

实例:

127.0.0.1:6379> subscribe mychannel
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "mychannel"
3) (integer) 1
1) "message"
2) "mychannel"
3) "mychannel message"

6. unsubscribe: 退订给定的频道。

指示客户端退订给定的频道,若没有指定频道,则退订所有频道.

如果没有频道被指定,即,一个无参数的 UNSUBSCRIBE 调用被执行,那么客户端使用 SUBSCRIBE 命令订阅的所有频道都会被退订。 在这种情况下,命令会返回一个信息,告知客户端所有被退订的频道。

语法:

 UNSUBSCRIBE channel [channel ...]  

返回值:这个命令在不同的客户端中有不同的表现。

实例:

127.0.0.1:6379> unsubscribe mychannel
1) "unsubscribe"
2) "mychannel"
3) (integer) 0
127.0.0.1:6379> unsubscribe
1) "unsubscribe"
2) (nil)
3) (integer) 0

Sentinel哨兵中的应用

Sentinel服务器与Master服务器/Slave服务器之间的订阅发布系统是Sentinel监控过程的一个重要环节,通过订阅发布系统达到监控服务器状态的作用。其运行原理与上面的客户端服务器之间的订阅机制无太大区别,都是基于网络连接的数据传输。Sentinel之间的通信也是通过Sentinel与服务器之间的这个订阅发布系统实现的,一个Sentinel通过服务器的频道发送信息,其他Sentinel就会接收到。

原文地址:https://www.cnblogs.com/huige185/p/14227312.html