通过前面对于Redis订阅发布功能的介绍,我们了解到,Redis客户端可以订阅若干个频道用于接收其他客户端在该频道上发布的消息。在本篇文章之中,将要介绍Redis之中基于订阅发布功能而实现的键空间事件通知功能。使用这个功能,客户端可以监听某一个给定键上发生的事件;或者监听某一类型操作的事件,借此功能,客户端可以自行触发在某些键发生变化时,或者某些操作发生时的回调逻辑。然而正如订阅发布功能的不可靠性,基于这个功能实现的事件通知功能同样是不可靠的,当客户端断开连接再重新连接之后,在断开阶段服务器产生的事件将会丢失。
Redis键空间事件通知的本质是客户端订阅特定格式名称的频道,而服务器在事件发生时向特定格式名称的频道上发布消息,通过订阅发布功能使客户端收到事件通知。
Redis将通知分为两个大类:
键空间通知,其含义是键空间上的键发生了变化之后而产生的通知,对于这类通知,实际上是服务器执行了一条PUBLISH的命令:
PUBLISH keyspace@:key event
其含义为,频道名为__keyspace@<db>__:key
,表示这个频道上发布的是db
编号数据库上的key
这个键的消息,而event
则是这条消息代表的key
上的操作。
键事件通知,执行了某种操作之后而产生的通知,对于这类通知,服务器相当于执行了下面这条命令:
PUBLISH keyevent@:event key
这里频道名为__keyevent@<db>__:event
,表示这个频道上发布的是db
编号数据库上的关于event
这个事件的消息,这个事件可以是一个命令,也可以是类似过期或者淘汰这样的事件,key
则是这条消息event
对应操作的键。
Redis可以通过配置文件来配置键空间的事件通知功能,对应的配置项为:
notify-keyspace-events ""
这里notify-keyspace-events
通过字符串来配置开启事件通知的种类:
K,开启键空间事件通知,发布在
__keyspace@<db>__:*
频道上E,开启键事件事件通知,发布在
__keyevent@<db>__:*
频道上g,开启一般命令事件通知,通常是不指定对象类型的命令,例如DEL,RENAME等等
$,开启操作字符串命令事件通知
l,开启操作列表对象事件通知
s,开启操作集合对象事件通知
h,开启操作散列对象事件通知
z,开启操作有序集合对象事件通知
x,开启键的过期事件通知
e,开启键的淘汰事件通知
A,等价于
g$lshzxe
,如果notify-keyspace-events
被设置为AKE事件,则意味着开启所有键以及所有事件的通知。
例如,
notify-keyspace-events Elg
表示,开启一般命令事件的通知,以及列表对象命令通知;同时也允许客户端订阅上述两类事件通知。
上面这些可已开启的通知类型,在服务器中是以掩码形式存储在redisServer.notify_keyspace_events
这个服务器数据字段之中。
通过前面对于PUBLISH命令对应实现函数的介绍,我们知道,发布一个消息,需要对服务器上redisServer.pubsub_patterns
这个双端链表进行遍历,同时对于里面每一个pubsubPattern.pattern
进行字符串匹配。如果只要涉及到键的变化或者是事件的产生就要调用PUBLISH命令进行消息发布的话,那么势必降低服务器的性能,因此默认的情况下,Redis是关闭事件通知这个功能的,只有在确切需要的时候,才会开启这个功能。
Redis对于事件通知,是通过下面这个函数实现的:
void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid);
这个函数首先会通过通知类型type
与redisServer.notify_keyspace_events
来判断这个类型的通知是否开启,如果没有开启则直接返回。如果通知类型已经开启,那么服务器根据参数构造出频道名,并通过调用pubsubPublishMessage
函数,将消息发布到特定频道上。
这个函数被调用的场景非常广泛,在很多命令的接口之中都对被调用,例如INCR命令的实现函数:
void incrDecrCommand(client *c, long long incr){ ... notifyKeyspaceEvent(NOTIFY_STRING,"incrby",c->argv[1],c->db->id); ...}




