Redis数据库的通知主要用来获取数据库中的键的变化以及数据库中命令的执行情况。
要想使用redis数据库中的通知的功能则需要在redis.conf配置文件中进行相应的配置
键的变化通知用官方的语句称为键空间通知
命令的执行情况通知用官方的语句称为键事件通知
配置文件redis.conf中的notify-keyspace-events
选项决定了服务器发送通知的类型
以下列举一些常见的配置
notify-keyspace-events AKE
服务器发送所有的键空间和键事件
notify-keyspace-events AK
服务器发送所有的键空间
notify-keyspace-events AE
服务器发送所有的键事件
notify-keyspace-events K$
服务器只发送所有的有关字符串键有关的键空间通知
notify-keyspace-events Elg
服务器只发送所有的有关列表键有关的基础命令通知
配置为notify-keyspace-events AKE
下进行有关的测试
以下为有关键空间通知的相关示例
127.0.0.1:6381> SUBSCRIBE __keyspace@0__:msg
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "__keyspace@0__:msg"
3) (integer) 1
我们再开启一个客户端执行相应的命令
127.0.0.1:6381> set msg hello
OK
127.0.0.1:6381> expire msg 10000
(integer) 1
127.0.0.1:6381> del msg
(integer) 1
则相应的服务器的输出依次如下
1) "message"
2) "__keyspace@0__:msg"
3) "set"
1) "message"
2) "__keyspace@0__:msg"
3) "expire"
1) "message"
2) "__keyspace@0__:msg"
3) "del"
以下为有关键事件通知的相关示例(del命令)
127.0.0.1:6381> SUBSCRIBE __keyevent@0__:del
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "__keyevent@0__:del"
3) (integer) 1
我们在另外一个客户端执行相应的命令如下
127.0.0.1:6381> set msg hello
OK
127.0.0.1:6381> del msg
(integer) 1
127.0.0.1:6381> lpush list 0 1 2
(integer) 3
127.0.0.1:6381> del list
(integer) 1
则对应的服务器的输出如下
1) "message"
2) "__keyevent@0__:del"
3) "msg"
1) "message"
2) "__keyevent@0__:del"
3) "list"
那么数据库的发送通知功能是怎样实现的呢?
在redis源码中的notify.c/notifyKeyspaceEvent
函数的源码如下
void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) {
sds chan;
robj *chanobj, *eventobj;
int len = -1;
char buf[24];
/* 如果给定的通知不是服务器允许发送的通知,则直接返回 */
if (!(server.notify_keyspace_events & type)) return;
eventobj = createStringObject(event,strlen(event));
/* 发送键空间通知 */
if (server.notify_keyspace_events & NOTIFY_KEYSPACE) {
chan = sdsnewlen("__keyspace@",11);
len = ll2string(buf,sizeof(buf),dbid);
chan = sdscatlen(chan, buf, len);
chan = sdscatlen(chan, "__:", 3);
chan = sdscatsds(chan, key->ptr);
//创建频道名字
chanobj = createObject(OBJ_STRING, chan);
//发送通知
pubsubPublishMessage(chanobj, eventobj);
decrRefCount(chanobj);
}
/* 发送键事件通知 */
/**
* server.notify_keyspace_events 为redis.conf中的配置内容
*/
if (server.notify_keyspace_events & NOTIFY_KEYEVENT) {
chan = sdsnewlen("__keyevent@",11);
if (len == -1) len = ll2string(buf,sizeof(buf),dbid);
chan = sdscatlen(chan, buf, len);
chan = sdscatlen(chan, "__:", 3);
chan = sdscatsds(chan, eventobj->ptr);
//创建频道名字
chanobj = createObject(OBJ_STRING, chan);
//发送通知
pubsubPublishMessage(chanobj, key);
decrRefCount(chanobj);
}
decrRefCount(eventobj);
}
其中参数type
表示的是将要发送的通知的类型
程序会将其与redis.conf中notify-keyspace-events
配置的参数经行对比,从而决定是否发送相应的通知
event
:事件的名称
key
:产生事件的键
dbid
:产生事件的数据库编号
每当一个命令需要发送数据库通知的时候,该命令就会调用notifyKeyspaceEvent()
函数。
我们以SADD
命令为例,其源码如下(t_set.c/saddCommand
)
void saddCommand(client *c) {
robj *set;
int j, added = 0;
set = lookupKeyWrite(c->db,c->argv[1]);
if (set == NULL) {
set = setTypeCreate(c->argv[2]->ptr);
dbAdd(c->db,c->argv[1],set);
} else {
if (set->type != OBJ_SET) {
addReply(c,shared.wrongtypeerr);
return;
}
}
for (j = 2; j < c->argc; j++) {
if (setTypeAdd(set,c->argv[j]->ptr)) added++;
}
/* 如果有至少一个元素被成功添加,那么执行以下程序 */
if (added) {
signalModifiedKey(c->db,c->argv[1]);
//发送事件通知
notifyKeyspaceEvent(NOTIFY_SET,"sadd",c->argv[1],c->db->id);
}
server.dirty += added;
addReplyLongLong(c,added);
}
我们知道发送通知的type
为NOTIFY_SET
所有的通知类型的定义为
/* Keyspace changes notification classes. Every class is associated with a
* character for configuration purposes. */
#define NOTIFY_KEYSPACE (1<<0) /* K */
#define NOTIFY_KEYEVENT (1<<1) /* E */
#define NOTIFY_GENERIC (1<<2) /* g */
#define NOTIFY_STRING (1<<3) /* $ */
#define NOTIFY_LIST (1<<4) /* l */
#define NOTIFY_SET (1<<5) /* s */
#define NOTIFY_HASH (1<<6) /* h */
#define NOTIFY_ZSET (1<<7) /* z */
#define NOTIFY_EXPIRED (1<<8) /* x */
#define NOTIFY_EVICTED (1<<9) /* e */
#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED) /* A */
另外执行DEL
命令时调用发送事件通知函数的参数如下
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[j],c->db->id);
可知通知的类型为通用类型NOTIFY_GENERIC