Análisis y comentarios del código fuente de Redis (15) ---- implementación de la función de notificación y combate real (notificar)

Implementación de la función de notificación de Redis y combate real

  1. Introducción a la función de notificación El
    cliente puede recibir eventos que cambian el conjunto de datos de Redis de alguna manera a través de las funciones de suscripción y publicación (pub / sub).

La función actual de suscripción y publicación de Redis adopta una estrategia de "disparar y olvidar". Cuando un cliente que se suscribe a un evento se desconecta, perderá todos los eventos distribuidos durante la desconexión.

  1. Tipos de notificaciones Los tipos de
    funciones de notificación son:

Notificación de espacio de clave (notificación de espacio de clave) Notificación
de evento de clave (notificación de evento de clave)

//notify.c
#define NOTIFY_KEYSPACE (1<<0)    /* K */   //键空间通知
#define NOTIFY_KEYEVENT (1<<1)    /* E */   //键事件通知

El formato de estas dos notificaciones es el siguiente:

__keyspace@<db>__:<key> <event> notifications.  //键空间通知格式
__keyevente@<db>__:<event> <key> notifications. //键事件通知格式

El código fuente para construir estos dos formatos de notificación es el siguiente:

// event 是一个字符串类型的事件名
// key 是一个对象代表一个键名
// dbid 是数据库id
void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) {
    
    
    sds chan;
    robj *chanobj, *eventobj;
    int len = -1;
    char buf[24];

    /* If notifications for this class of events are off, return ASAP. */
    // 如果notify_keyspace_events中配置了不发送type类型的通知,则直接返回
    // notify_keyspace_events值为 一个type的亦或值,type保存有不发送的通知
    if (!(server.notify_keyspace_events & type)) return;

    // 创建一个事件通知对象
    eventobj = createStringObject(event,strlen(event));

    /* __keyspace@<db>__:<key> <event> notifications. */
    // 发送 键空间 通知
    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);
        // 通过publish命令发送频道对象chanobj和事件对象eventobj通知
        pubsubPublishMessage(chanobj, eventobj);
        decrRefCount(chanobj);  //释放对象
    }

    /* __keyevente@<db>__:<event> <key> notifications. */
    // 发送 键事件 通知
    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);
        // 通过publish命令发送频道对象chanobj和键key通知
        pubsubPublishMessage(chanobj, key);
        decrRefCount(chanobj);  //释放对象
    }
    decrRefCount(eventobj); //释放事件对象
}
  1. La configuración de la función de notificación La configuración de la
    función de notificación está desactivada de forma predeterminada, porque la mayoría de los usuarios no necesitan esta función, porque habrá una sobrecarga adicional para activar esta función. Dos formas de activar la función de notificación

Modifique notify-keyspace-events
CONFIG SET notify-keyspace-events en el archivo de configuración redis.conf. El
notify-keyspace-eventsparámetro de carácter puede ser cualquier combinación de los siguientes caracteres, que especifica qué tipo de notificación debe enviar el servidor:

Inserte la descripción de la imagen aquí
Debe haber al menos una K o E en los parámetros de entrada; de lo contrario, no importa cuáles sean los otros parámetros, no se distribuirá ninguna notificación. Por ejemplo, si se configura KEA, significa enviar todo tipo de notificaciones.

Las definiciones del código fuente de estos caracteres:

// 键空间通知的类型,每个类型都关联着一个有目的的字符
#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 */   //所有键通知

Estos caracteres generalmente se guardan mediante la operación OR bit a bit de varios caracteres utilizando un parámetro de banderas de tipo int. Por lo tanto, implica la conversión mutua de banderas y cadenas.

Cadena a banderas

// 对传入的字符串参数进行分析,返回一个flags,flags保存字符串每个字符所映射的键空间事件类型
int keyspaceEventsStringToFlags(char *classes) {
    
    
    char *p = classes;
    int c, flags = 0;

    while((c = *p++) != '\0') {
    
    
        switch(c) {
    
    
        case 'A': flags |= NOTIFY_ALL; break;
        case 'g': flags |= NOTIFY_GENERIC; break;
        case '$': flags |= NOTIFY_STRING; break;
        case 'l': flags |= NOTIFY_LIST; break;
        case 's': flags |= NOTIFY_SET; break;
        case 'h': flags |= NOTIFY_HASH; break;
        case 'z': flags |= NOTIFY_ZSET; break;
        case 'x': flags |= NOTIFY_EXPIRED; break;
        case 'e': flags |= NOTIFY_EVICTED; break;
        case 'K': flags |= NOTIFY_KEYSPACE; break;
        case 'E': flags |= NOTIFY_KEYEVENT; break;
        default: return -1;
        }
    }
    return flags;
}

banderas para encadenar

// 根据flags返回一个字符串,字符串中的字符就是设置flags的字符
sds keyspaceEventsFlagsToString(int flags) {
    
    
    sds res;

    res = sdsempty();
    if ((flags & NOTIFY_ALL) == NOTIFY_ALL) {
    
    
        res = sdscatlen(res,"A",1);
    } else {
    
    
        if (flags & NOTIFY_GENERIC) res = sdscatlen(res,"g",1);
        if (flags & NOTIFY_STRING) res = sdscatlen(res,"$",1);
        if (flags & NOTIFY_LIST) res = sdscatlen(res,"l",1);
        if (flags & NOTIFY_SET) res = sdscatlen(res,"s",1);
        if (flags & NOTIFY_HASH) res = sdscatlen(res,"h",1);
        if (flags & NOTIFY_ZSET) res = sdscatlen(res,"z",1);
        if (flags & NOTIFY_EXPIRED) res = sdscatlen(res,"x",1);
        if (flags & NOTIFY_EVICTED) res = sdscatlen(res,"e",1);
    }
    if (flags & NOTIFY_KEYSPACE) res = sdscatlen(res,"K",1);
    if (flags & NOTIFY_KEYEVENT) res = sdscatlen(res,"E",1);
    return res;
}
  1. Función de notificación combate real
    Publicar función de suscripción comando método de uso de la tecla

Cliente 1

127.0.0.1:6379> PSUBSCRIBE __key*           //执行接收所有类型的通知
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__key*"
3) (integer) 1
//此时客户端阻塞等待消息的发布

Cliente 2

127.0.0.1:6379> CONFIG SET notify-keyspace-events KEA   //设置接受所有通知,会改变server.notify_keyspace_events值
OK
127.0.0.1:6379> set number 888
OK
127.0.0.1:6379> DEL number 888
(integer) 1
127.0.0.1:6379> HSET hash field1 value1
(integer) 1
127.0.0.1:6379> EXPIRE hash 5           //设置生存时间为5秒
(integer) 1

Cliente 1

127.0.0.1:6379> PSUBSCRIBE __key*           //执行接收所有类型的通知
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__key*"
3) (integer) 1
//此时客户端阻塞等待消息的发布,当客户端2发布消息时,会打印一下信息
1) "pmessage"                   //set number 888 命令所生成的通知
2) "__key*"
3) "__keyspace@0__:number"
4) "set" 
1) "pmessage"
2) "__key*"
3) "__keyevent@0__:set"
4) "number"

1) "pmessage"                   //DEL number 888 命令所生成的通知
2) "__key*"
3) "__keyspace@0__:number"
4) "del"  
1) "pmessage"
2) "__key*"
3) "__keyevent@0__:del"
4) "number"

1) "pmessage"                   //HSET hash field1 value1 命令所生成的通知
2) "__key*"
3) "__keyspace@0__:hash"
4) "hset"  
1) "pmessage"
2) "__key*"
3) "__keyevent@0__:hset"
4) "hash"

1) "pmessage"                   //EXPIRE hash 5 命令所生成的通知
2) "__key*"
3) "__keyspace@0__:hash"
4) "expire" 
1) "pmessage"
2) "__key*"
3) "__keyevent@0__:expire"
4) "hash"

1) "pmessage"                   //当hash键的5秒的生存时间到时后,自动发送 expired 通知
2) "__key*"
3) "__keyspace@0__:hash"
4) "expired"  
1) "pmessage"
2) "__key*"
3) "__keyevent@0__:expired"
4) "hash"

Supongo que te gusta

Origin blog.csdn.net/qq_26249609/article/details/103986173
Recomendado
Clasificación