redis数据库中的通知

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);
}

我们知道发送通知的typeNOTIFY_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

猜你喜欢

转载自blog.csdn.net/zjq_1314520/article/details/79301896