zoukankan      html  css  js  c++  java
  • 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中有专门针对这种情况的慢消息机

  • 相关阅读:
    「七天自制PHP框架」第四天:模型关联
    「七天自制PHP框架」第三天:PHP实现的设计模式
    「七天自制PHP框架」第二天:模型与数据库
    一个例子简要说明include和require的区别
    解读Laravel,看PHP如何实现Facade?
    Laravel是怎么实现autoload的?
    Laravel表单提交
    Laravel的console使用方法
    PHP控制反转(IOC)和依赖注入(DI)
    PHP解耦的三重境界(浅谈服务容器)
  • 原文地址:https://www.cnblogs.com/lx823706/p/7655681.html
Copyright © 2011-2022 走看看