Redis系列—Redis事件订阅

  Redis从2.X版本开始,就支持一种基于非持久化消息的、使用发布/订阅模式实现的事件通知机制。所谓基于非连接保持,是因为一旦消息订阅者由于各种异常情况而被迫断开连接,在其重新连接后,其离线期间的事件是无法被重新通知的(一些redis资料中也称为即发即弃)。而其使用的发布/订阅模式,意味着其机制并不是由订阅者周期性的从Redis服务拉取事件通知,而是由Redis服务主动推送事件通知到符合条件的若干订阅者

Redis中的事件功能可以提供两种不同的功能:

  一类是基于Channel的消息事件,这一类消息和Redis中存储的Keys没有太多关联,也就是说即使不在Redis中存储任何Keys信息,这类消息事件也可以独立使用。

  另一类消息事件可以对(也可以不对)Redis中存储的Keys信息的变化事件进行通知,可以用来向订阅者通知Redis中符合订阅条件的Keys的各种事件。Redis服务的事件功能在实际场景中虽然使用得不多,不过还是可以找到案例,例如服务治理框架DUBBO默认情况下使用Zookeeper作为各节点的服务协调装置,但可以通过更改DUBBO的配置,将Zookeeper更换为Redis。

一、publish 和 subscribe

  我们先从比较简单的publish命令和subscribe命令开始介绍,因为这组命令所涉及到的Channel(通道)和Redis中存储的数据相对独立。publish命令由发送者使用,负责向指定的Channel发送消息;subscribe命令由订阅者使用,负责从指定的一个或者多个Channel中获取消息。

这里写图片描述

以下是 publish 命令和 subscribe 命令的使用示例:

// 该命令向指定的channel名字发送一条消息(字符串)
PUBLISH channel message 
// 例如:向名叫FM955的频道发送一条消息,消息信息为“hello!”
PUBLISH FM955  "hello!"
// 再例如:向名叫FM900的频道发送一条消息,消息信息为“ doit!”
PUBLISH FM900 "doit!"
// 该命令可以开始向指定的一个或者多个channel订阅消息
SUBSCRIBE channel [channel ...]
// 例如:向名叫FM955的频道订阅消息
SUBSCRIBE FM955
// 再例如:向名叫FM955、FM900的两个频道订阅消息
SUBSCRIBE FM955 FM900

  如果您使用需要使用publish命令和subscribe命令,您并不需要对Redis服务的配置信息做任何更改。以下示例将向读者展示两个命令的简单使用方式——前提是您的Redis服务已经启动好了:

  • 由客户端A充当订阅者,在ChannelA和ChannelB两个通道上订阅消息
-- 我们使用的Redis服务地址为192.168.61.140,端口为默认值
[root@kp2 ~]# redis-cli -h 192.168.61.140
192.168.61.140:6379> SUBSCRIBE ChannelA ChannelB
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "ChannelA"
3) (integer) 1
1) "subscribe"
2) "ChannelB"
3) (integer) 2
  • 有客户端B从当订阅者,通过ChannelB发送消息给所有订阅者
-- 连接到Redis服务器后,直接运行PUBLISH命令,发送信息
[root@kp1 ~]# redis-cli -h 192.168.61.140
192.168.61.140:6379> PUBLISH ChannelB "hello"
(integer) 1
  • 以下是订阅者客户端A所受到的message信息
......
-- 这时订阅者收到消息如下:
1) "message"
2) "ChannelB"
3) "hello"

  从以上示例中可以看到,客户端A确实收到了客户端B所发送的消息信息,并且收到三行信息。这三行信息分别表示消息类型、消息通道和消息内容。注意,以上介绍的这组publish命令和subscribe命令的操作过程并没有对Redis服务中已存储的任何Keys信息产生影响。

二、模式订阅psubscribe

  Redis中还支持一种模式订阅,它主要依靠psubscribe命令向技术人员提供订阅功能。模式订阅psubscribe最大的特点是,它除了可以通过Channel订阅消息以外,还可以配合配置命令来进行Keys信息变化的事件通知。

  模式订阅psubscribe的Channel订阅和subscribe命令类似,这里给出一个命令格式,就不再多做介绍了(可参考上文对subscribe命令的介绍):

// 该命令可以开始向指定的一个或者多个channel订阅消息
// 具体使用示例可参见SUBSCRIBE命令
PSUBSCRIBE channel [channel ...]

  模式订阅psubscribe对Keys变化事件的支持分为两种类型:keyspace(键空间通知)和keyevent(键事件通知),这两类事件都是依靠Key的变化触发的,而关键的区别在于事件描述的焦点,举例说明:

  当Redis服务中0号数据库的MyKey键被删除时,键空间和键事件向模式订阅者分别发送的消息格式如下:

// 以下命令可订阅键空间通知
// 订阅0号数据库任何Key信息的变化
192.168.61.140:6379> psubscribe __keyspace@0__:*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__keyspace@0__:*"
3) (integer) 1
// 出现以上信息,说明订阅成功
// 当其他客户端执行 set mykey 123456 时,该订阅可收到以下信息
1) "pmessage"
2) "__keyspace@0__:*"
3) "__keyspace@0__:mykey"
4) "set"

  以上收到的订阅信息,其描述可以概括为:“mykey的键空间发生了事件,事件为set”。这样的事件描述着重于key的名称,并且告诉客户端key的事件为set。我们再来看看订阅键事件通知时,发生同样事件所得到的订阅信息:

// 以下命令可订阅键事件通知
// 订阅0号数据库任何事件的变化
192.168.61.140:6379> psubscribe __keyevent@0__:*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__keyevent@0__:*"
3) (integer) 1
// 出现以上信息,说明订阅成功
// 当其他客户端执行 set mykey 123456 时,该订阅可收到以下信息
1) "pmessage"
2) "__keyevent@0__:*"
3) "__keyevent@0__:set"
4) "mykey"

  以上收到的订阅信息中事件是主体,其信息可以概括为:“0号数据库发生了set事件,发生这个事件的key信息为mykey”。

三、模式订阅的配置

1、配置和通配符

  要使用psubscribe命令进行键事件的订阅,就首先需要在Redis的主配置文件中对模式订阅进行设定。注意,如果您只是使用psubscribe命令通过Channel发送消息到订阅者,或者更单纯的使用publish命令和subscribe命令组合通过Channel发送和接收消息,就不需要进行这样的配置。

  默认情况下Redis服务下的键空间通知和键事件通知都是关闭的。在redis.conf文件下,有专门的“EVENT NOTIFICATION”区域进行设定,设置的格式为:

......
notify-keyspace-events [通配符]
......

通配符的定义描述如下:

  • K:启用keyspace键空间通知,客户端可以使用keyspace@为前缀的格式使用订阅功能;
  • E:启用keyevent键事件通知,客户端可以使用keyevent@为前缀的格式使用订阅功能;
  • g:监控一般性事件,包括但不限于对del,expire,rename事件的监控;
  • $:启用对字符串格式(即一般K-V结构)命令的监控;
  • l:启用对List数据结构命令的监控;
  • s:启用对Set数据结构命令的监控;
  • h:启用对Hash数据结构命令的监控;
  • z:启用对ZSet数据结构命令的监控;
  • x:启用对过期事件的监控;
  • e:启用对驱逐事件的监控,当某个键因maxmemory达到设置时,使用策略进行内存清理,会产生这个事件;
  • A:g$lshzxe通配符组合的别名,也就是说”AKE”这样的通配符组合,意味着所有事件。

以下的几个实例说明了配置格式中通配符的用法:

// 监控任何数据格式的所有事件,包括键空间通知和键事件通知
notify-keyspace-events "AKE"

// 只监控字符串结构的所有事件,包括键空间通知和键事件通知
notify-keyspace-events "g$KExe"

// 只监控所有键事件通知
notify-keyspace-events "AE"

// 只监控Hash数据解构的键空间通知
notify-keyspace-events "ghKxe"

// 只监控Set数据结构的键事件通知
notify-keyspace-events "gsExe"

  注意,在Redis主配置文件中进行事件通知的配置,其配置效果是全局化的。也就是说所有连接到Redis服务的客户端都会使用这样的Key事件通知逻辑。但如果单独需要为某一个客户端会话设置独立的Key事件通知逻辑,则可以在客户端成功连接Redis服务后,使用类似如下的命令进行设置:

......
192.168.61.140:6379> config set notify-keyspace-events KEA  
OK 

2、键事件订阅

  完成键事件的配置后,就可以使用psubscribe命令在客户端订阅消息通知了。这个过程还是需要使用通配符参数,才能完成订阅指定。通配符格式如下所示:

psubscribe __[keyspace|keyevent]@<db>__:[prefix]

// 例如:
// 订阅0号数据库中,所有的键变化事件,进行键空间通知
psubscribe __keyspace@0__:*

// 订阅0号数据库,所有的键变化事件,进行键空间通知和键事件通知
psubscribe __key*@0__:*

  注意,就如上文所提到的那样,客户端能够进行键信息变化事件订阅的前提是Redis服务端或者这个客户端会话本身开启了相应配置。以下举例说明psubscribe命令中参数的使用方式:

// 注意,Redis服务上的配置信息如下
// notify-keyspace-events "gsExe"
// 即是说只允许监控Set结构的所有事件,并且之启用了键事件通知,没有启用键空间通知。

// 客户端使用以下命令开始订阅Key的变化事件
192.168.61.140:6379> psubscribe __key*@0__:*
// 以上命令订阅了0号数据库所有键信息的变化通知,包括键事件通知和键空间通知
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__key*@0__:*"
3) (integer) 1

// 接着,已连接到Redis服务上的另一个客户端执行了如下命令
// > sadd mysetkey rt
// 那么收到的消息通知为
1) "pmessage"
2) "__key*@0__:*"
3) "__keyevent@0__:sadd"
4) "mysetkey"

以上实例操作中有两个问题需要单独进行说明:

  当客户端使用psubscribe命令进行订阅时(psubscribe key*@0:*),实际上是连同keyspace(键空间通知)和keyevent(键事件通知)一起订阅了。那么按照上文介绍的内容来说,这个订阅者本该收到两条事件消息。一条消息的描述重点在key上,另一条消息的描述重点在sadd事件上。但实际情况是,这个订阅者只收到了以描述重点在事件上的键事件通知。这是因为在以上实例中特别说明的一点:Redis服务端只开启键事件通知的配置。所以无论客户端如何订阅键空间通知,也收不到任何消息。

  另外,包括Redis官方资料在内的资料都在阐述这样一个事实,既是通过sadd命令对一个Set结构中的元素进行变更和直接通过“PUBLISH keyevent@0:sadd mysetkey”这样的命令向订阅者发送消息,在消息订阅者看来效果都是一样。但是这两种不同的操作过程对于Redis存储的Key数据,则是完全不一样的。前者的操作方式会改变Redis中存储的数据状况,但后者则不会。

四、Redis订阅/发布功能的不足

Redis提供的订阅/发布功能并不完美,更不能和ActiveMQ/RabbitMQ提供的订阅/发布功能相提并论。

  • 首先这些消息并没有持久化机制,属于即发即弃模式。也就是说它们不能像ActiveMQ中的消息那样保证持久化消息订阅者不会错过任何消息,无论这些消息订阅者是否随时在线。

  • 由于本来就是即发即弃的消息模式,所以Redis也不需要专门制定消息的备份和恢复机制。

  • 也是由于即发即弃的消息模式,所以Redis也没有必要专门对使用订阅/发布功能的客户端连接进行识别,用来明确该客户端连接的ID是否在之前已经连接过Redis服务了。ActiveMQ中保持持续通知的功能的前提,就是能够识别客户端连接ID的历史连接情况,以便确定哪些订阅消息这个客户端还没有处理。

  • Redis当前版本有一个简单的事务机制,这个事务机制可以用于PUBLISH命令。但是完全没有ActiveMQ中对事务机制和ACK机制那么强的支持。而在我写作的“系统间通讯”专题中,专门讲到了ActiveMQ的ACK机制和事务机制。

  • Redis也没有为发布者和订阅者准备保证消息性能的任何方案,例如在大量消息同时到达Redis服务是,如果消息订阅者来不及完成消费,就可能导致消息堆积。而ActiveMQ中有专门针对这种情况的慢消息机制。

你可能感兴趣的:(Redis)