Cómo cronometrar la sesión de Nodejs Redis
Para tomar el ejemplo más simple, si un usuario recomienda a otro usuario, establezca una tarea después de 24 horas para verificar si el usuario recomendado está registrado. Si no, envíele un mensaje de texto. Σgt;-(〃°ω°〃)?
Pensamientos iniciales
Al principio, quería crear este temporizador en la memoria y llamarlo directamente.
Teniendo en cuenta que el tiempo de Node.js no es tan preciso (ya sea setTimeout o setInterval), planeo mantener la cola del temporizador yo mismo.
Teniendo en cuenta que los objetos nativos de Node.js ocupan mucha memoria, utilizo objetos JSON para almacenar los datos. Estoy usando un objeto JSON para almacenar un diccionario con aproximadamente 120.000 entradas, y el archivo original tiene solo unos 5 o 6 megabytes, pero usar objetos nativos de Node.js para almacenarlo en realidad requiere 5 o 6 megabytes de memoria, así que planeo utilizar C para escribir un componente complementario para esta cola de temporizador.
Tenga en cuenta que cada vez que se inserta una tarea, puede estar en medio de una tarea existente o la tarea puede estar en medio de una tarea existente.
Teniendo en cuenta que en cualquier momento una tarea insertada puede estar antes o después de una tarea existente, voy a escribir un pequeño montón raíz en C. Cada vez que un usuario accede a una tarea, ésta se inserta en el montón.
Si sigue el enfoque anterior y los requisitos de tiempo no son muy estrictos, entonces es un proceso constante.nextTick().
Ejecute dicha función en Process.nextTick():
Obtenga la tarea en la parte superior del montón desde el montón raíz pequeño y procese hasta el tiempo de ejecución de la tarea en la parte superior del montón es mayor que la hora actual.
Continúe usando Process.nextTick() para obtener el siguiente tick para realizar el procesamiento en el paso 1.
Entonces, lo que termina siendo un proceso de insertar tareas en el montón de rootlets y consumirlas usando Process.nextTick().
Finalmente, para tener en cuenta la pérdida de datos en la memoria cuando se reinicia el programa, también debes hacer algo de persistencia: insertar una copia del middleware de persistencia cada vez que se inserta una tarea, por ejemplo. por ejemplo MySQL, MongoDB, Redis, Riak, etc., así como cualquier dependencia de tres vías. Al consumir tareas, estos datos de tareas se eliminan del middleware.
Esto significa que sólo las tareas actualmente no finalizadas se almacenan en el middleware. Cada vez que se reinicia el programa, lee todas las tareas del middleware y reconstruye el montón, y luego puede continuar ejecutándose.
Si no hemos descubierto este uso de Redis, entonces este es el proceso de implementación de tareas programadas.
El propósito de Redis
Después de la versión 2.8.0 de Redis, se introdujo una nueva función Notificaciones de espacio de claves de Redis. Esta función se utiliza junto con SUBSCRIBE después de la versión 2.0.0 para completar esto. es una tarea programada. Desde la versión 2.0.0, esta función se puede utilizar con SUBSCRIBE para completar esta tarea programada en segundos.
Publicar/Suscribirse
Redis introdujo el comando Pub/Sub después de 2.0.0, que está relacionado con el envío de mensajes hacia y desde un canal específico en Redis (una cola de mensajes simple) Canales obtener aproximadamente el mismo valor.
Cola de mensajes
Por ejemplo, para enviar un mensaje al canal foo, simplemente usarías:
PUBLISH foo bar
javascript
var Redis = require("ioredis");
var sub = nueva función(err) {
if(err) proceso.exit(4);
sub.subscribe("foo", function() {
//...canal de suscripción exitoso
});
}); /p >
});
//Escuchar mensajes de "foo"
sub.on("message", function(channel, msg) {
console.log(channel, msg);
});
Notificación de espacio clave de Redis
Hay muchos eventos dentro de Redis, como caducidad de clave, eliminación de clave, etc. Puede configurar funciones para que Redis envíe mensajes a canales específicos cuando se activen estos eventos.
En este artículo, el evento en el que debemos centrarnos es EXPIRE, que es un evento de vencimiento.
En términos generales, este proceso consiste en establecer un evento de caducidad para el conjunto de datos de Redis, de modo que una vez que la clave caduque, enviará un mensaje a un canal específico y luego consumirá el canal en su propio cliente.
Una vez que aparece una tarea programada más tarde, comprimirá el estado de la tarea en una clave y el tiempo de vencimiento es la diferencia de tiempo entre la ejecución de la tarea. Luego, cuando la clave caduca, es hora de ejecutar la tarea y Redis, naturalmente, enviará el mensaje de caducidad al cliente. Este es el papel de las tareas programadas.
Tipos de mensajes
Se pueden activar dos tipos de mensajes cuando se cumplen ciertas condiciones, y debes elegir cuál usar. Por ejemplo, si elimina una clave llamada foo en db 0, el sistema enviará el mensaje a dos canales. Uno es el canal del evento que envía el mensaje foo y el otro es el canal foo que envía el mensaje del. Están controlados. por los dos canales del sistema impulsado por Xiaokou, es equivalente al comando:
PUBLISH __keyspace@0__: foo del PUBLISH __keyevent@0__: del foo
El canal para. empujar del a foo se llama __keyspace@0__: foo, es decir, "__keyspace@" DB_NUMBER "__:". KEY_NAME; y el canal de del se llama "__keyevent@" DB_ NUMBER "__:" .
Configuración
Incluso si su versión de Redis cumple con los requisitos, Redis aún la desactiva de forma predeterminada, por lo que debe modificar el archivo de configuración para activarla o cambiarlo directamente a través de comandos en la CLI. He aquí cómo hacerlo.
Primero, abra el archivo de configuración de Redis. La ubicación de este archivo puede variar entre sistemas e instalaciones, por ejemplo, en MacOS instalado a través de Brew, puede estar ubicado en /usr/local/etc/redis.conf en Ubuntu instalado a través de apt-get, que puede estar ubicado en; /etc/redis/. En Ubuntu, esto puede estar ubicado en /etc/redis/redis.conf. De todos modos, busque el archivo de configuración. O escriba su propio archivo de configuración directamente y especifique la dirección del archivo de configuración al inicio.
Luego busque la entrada llamada notify-keyspace-events. Si no puede encontrarla, también puede agregarla usted mismo. Su valor puede ser Ex, Klg, etc. Los significados de estas letras son los siguientes. Los significados de estas letras son los siguientes:
K , que representa un evento de espacio de claves, se utiliza para enviar información al canal __keyspace@lt;dbgt;__.
E, representa un evento clave. Utilice esta letra para enviar información al canal __keyevent@lt;dbgt;__.
g , que indica compatibilidad con algunos eventos de comando comunes, como DEL, EXPIRE, RENAME, etc.
$ , que indica compatibilidad con eventos para comandos relacionados con cadenas.
l , que representa eventos de comando relacionados con listas.
s , que indica soporte de eventos para instrucciones relacionadas con Set.
h , que indica compatibilidad con eventos de comando relacionados con hash.
z , que indica compatibilidad con eventos de comando relacionados con colecciones ordenadas.
x, evento de caducidad, que es diferente de EXPIRE en g. EXPIRE en g es un evento que se activa al ejecutar la instrucción ttl de la clave EXPIRE, y EXPIRE aquí es un evento que se activa cuando la clave caduca.
e, EXPIRE, evento que se activa cuando la clave se elimina debido a limitaciones de memoria.
A, alias de g$lshzxe. Es decir, AKE representa todos los eventos.
Combinando la lista anterior, puede reunir las cadenas de soporte de eventos requeridas; solo se requiere Ex en los requisitos, por lo que el elemento de configuración se ve así:
notify-keyspace-events Ej
Luego guarde el archivo de configuración e inicie Redis para habilitar la compatibilidad con eventos de caducidad.
Práctica
Comencemos con el creador de tareas. Dado que el evento de Redis solo pasa el nombre de la clave aquí, no el valor de la clave, y el nombre de la clave desapareció cuando se activa el evento de caducidad, no puede obtener el valor de la clave, y dado que el sistema principal y el sistema de tareas están distribuidos, usted Solo necesita ingresar toda la información que necesita en el nombre de la clave.
Uno de los diseños de nombres de clave más simples es el tipo de tarea ":" matriz de parámetros JSON.stringify. Alternativamente, puede reemplazar directamente el tipo de tarea con la ruta a la función deseada, por ejemplo, si lo necesita; Esta tarea se ejecuta bajo la función baz en el archivo task/foo/bar. De todos modos, solo necesita activar esta clave sin consultar la clave. Cuando realmente caduca, el sistema de tareas recibe el nombre de la clave, luego las analiza una por una, recibe el mensaje de que es necesario ejecutar task/foo/bar.baz y pasa este parámetro en la función net.
Por lo tanto, al recibir una tarea programada, obtenga el mensaje, el nombre de la función, el parámetro de tiempo de vencimiento y esta función.
/** Supongamos que redis es un objeto ioredis*/ p>
var sampleTaskMaker = function(message, func, timeout) {
message = JSON.stringify(message);
console.log("Recibió una nueva tarea: " , func, mensaje, "después de " tiempo de espera ".") )
// El uuid aquí es un paquete npm
// El propósito de generar un uuid único es evitar dos tareas de usarlo La misma función y parámetros, entonces
// Los nombres de las claves pueden repetirse y sobrescribirse
// La documentación para uuid es /package/node-uuid p>
. p> //
// ? es el delimitador, los dos puntos separan el uuid y el siguiente contenido, ? es el nombre de la función
// y la información
separador. func "?" mensaje;
var content = "";
redis.multi()
.set(clave, contenido)
.expire(clave, tiempo de espera)
.exec(function(err) {
if(err) {
console.error("Error al publicar EVENTO EXPIRE para "contenido);
console.error(err);
return;
}
});
};
// asignar es una función dentro de sugararjs
// Insertar db en {db} en la cadena
var subscribeKey = "__keyevent@{db}__:expired".select(1, function(err) {
var subscribeKey = "__keyevent@{db}__:expired".assign({ db: 1 }) función(err) {
if(err) proceso.exit(4);
sub.subscribe("foo", función() {
//...Suscripción al canal exitosamente
});
});
});
// Mensaje de monitoreo from "foo"
sub.on("message", sampleOnExpired);
NOTA: La razón para elegir db 1 es que una vez que active el detector de vencimiento, será enviado Todos los eventos de vencimiento en db. Por ejemplo, si escucha en una base de datos 0 normal, se enviarán eventos de caducidad no desencadenados por tareas y los nombres de clave resueltos serán incorrectos.
Finalmente está la función sampleOnExpired.
var sampleOnExpired = function(canal, clave) {
// UUID:?func?params
var body = key.split("?") ;
if(body.length lt; 3) return;
// Elimina el primer fragmento del cuerpo como func
var func = body[1 ] ;
// Saque los primeros dos dígitos y el resto del cuerpo puede dividirse porque tiene ? y está dividido, por lo que es necesario volver a armarlo
body.shift (); body.shift();
// Elimina el primer fragmento del cuerpo como func
var func = body[1]; p> // Elimina los dos primeros bits y el resto del cuerpo puede dividirse porque tiene ?shift();
var params = body.join("?"
// Luego pasa los parámetros a func para su ejecución
// func:
// ruta1/ruta2.func
func = func.split (" .");
if(func.length !== 2) {
console.error("Parámetros incorrectos para la tarea:", func.join("." ) , "-", params);
retorno
}
var ruta = func[0]; func[ 1];
var mod;
prueba {
mod = require("./tasks/" ruta);
} catch(e) {
console.error("Error al cargar el módulo", ruta);
console.error(e.stack);
devolver ;
}
proceso.nextTick(función() {
prueba {
mod[func].apply(null , JSON .parse(params));
} catch(e) {
console.error("Error al llamar a la función", ruta, "-", func, "- ", params);
console.error(e.stack);
return;
}
proceso.stack);
p>
}
}
});
}
Después de construir esto estante simple, lo que necesita es escribir una serie de funciones de ejecución de tareas y pasarlas a sampleTaskMaker al generar tareas.