que nodejs node desde curso cordoba cero avanzado node.js redis express race-condition

node.js - nodejs - node js vs java



connect-redis: cómo proteger el objeto de sesión contra la condición de carrera (1)

Bueno, implementar su propio almacenamiento podría ser la opción para usted. Esta documentación muestra que todo lo que necesita hacer es implementar tres métodos: .get , .set y .destroy (consulte el último párrafo). Sería algo como esto (usando la librería node-redis y modificando un poco la tienda connect-redis original ):

var redis = require("redis"), redis_client = redis.createClient(), session_prefix = ''session::'', lock_suffix = ''::lock'', threshold = 5000, wait_time = 250, oneDay = 86400; /* If timeout is greater then threshold, then we assume that one of the Redis Clients is dead and he cannot realese the lock. */ function CustomSessionStore(opts) { opts = opts || {}; var self = this; self.ttl = opts.ttl; // <---- used for setting timeout on session self.lock = function(sid, callback) { callback = callback || function(){}; var key = session_prefix + sid + lock_suffix; // try setting the lock with current Date redis_client.setnx(key, Date.now( ), function(err, res) { // some error handling? if (res) { // Everything''s fine, call callback. callback(); return; } // setnx failed, look at timeout redis_client.get(key, function(err, res) { // some error handling? if (parseInt(res) + threshold > Date.now( )) { // timeout, release the old lock and lock it redis_client.getset(key, Date.now( ), function(err, date) { if (parseInt(date) + threshold > Date.now()) { // ups, some one else was faster in acquiring lock setTimeout(function() { self.lock(sid, callback); }, wait_time); return; } callback(); }); return; } // it is not time yet, wait and try again later setTimeout(function() { self.lock(sid, callback); }, wait_time); }); }); }; self.unlock = function(sid, callback) { callback = callback || function(){}; var key = session_prefix + sid + lock_suffix; redis_client.del(key, function(err) { // some error handling? callback(); }); }; self.get = function(sid, callback) { callback = callback || function(){}; var key = session_prefix + sid; // lock the session self.lock(sid, function() { redis_client.get(key, function(err, data) { if (err) { callback(err); return; } try { callback(null, JSON.parse(data)); } catch(e) { callback(e); } }); }); }; self.set = function(sid, data, callback) { callback = callback || function(){}; try { // ttl used for expiration of session var maxAge = sess.cookie.maxAge , ttl = self.ttl , sess = JSON.stringify(sess); ttl = ttl || (''number'' == typeof maxAge ? maxAge / 1000 | 0 : oneDay); } catch(e) { callback(e); return; } var key = session_prefix + sid; redis_client.setex(key, ttl, data, function(err) { // unlock the session self.unlock(sid, function(_err) { callback(err || _err); }); }); }; self.destroy = function(sid, callback) { var key = session_prefix + sid; redis_client.del(key, function(err) { redis_client.unlock(sid, function(_err) { callback(err || _err); }); }); }; }

Nota al .lock : no implementé el manejo de errores para .lock y .unlock . ¡Te lo dejo a ti! :) Puede haber algunos errores menores (no tengo NodeJS en este momento y estoy escribiendo esto de mi memoria: D), pero debes entender la idea. Aquí está el enlace que contiene la discusión sobre cómo usar setnx para bloquear / desbloquear Redis.

La otra nota : es probable que desee realizar algún control personalizado de errores para las rutas, ya que si alguna ruta arroja una excepción, la sesión de Redis no se desbloqueará. El método .set siempre se llama como el último en la ruta, opuesto al método .get que Express llama al comienzo de la ruta (es por eso que bloqueo en .get y desbloqueo en .set ). Sin embargo, permanecerás bloqueado solo durante 5 segundos, por lo que no tiene por qué ser un problema. Recuerde ajustarlo a sus necesidades (especialmente variables de threshold y wait_time ).

Nota final : con este mecanismo, sus controladores de solicitud solo dispararán uno tras otro por usuario. Esto significa que no podrá ejecutar manejadores concurrentes por usuario. Esto podría ser un problema, por lo que la otra idea es mantener los datos fuera de la sesión y manejar el bloqueo / desbloqueo de forma manual. Después de todo, hay algunas cosas que deben manejarse manualmente.

¡Espero que ayude! ¡Buena suerte!

Estoy usando nodejs con connect-redis para almacenar los datos de la sesión.

Estoy guardando los datos del usuario en la sesión y los uso en la duración de la sesión.

Me di cuenta de que es posible tener una condición de carrera entre dos solicitudes que cambia los datos de la sesión.

Intenté usar redis-lock para bloquear la sesión, pero es un poco problemático para mí.

No quiero bloquear toda la sesión, sino bloquear solo la variable de sesión específica.

Descubrí que es imposible, y pensé en la dirección para resolverlo:

dejar de usar el objeto de sesión para almacenar los datos del usuario y guardar la variable directamente en el redis y el candado antes de usarlo.

Sé que puede funcionar, pero requerirá que administre todos los objetos manualmente en lugar de simplemente acceder a redis a través del objeto de sesión.

¿Pueden compartir conmigo las mejores prácticas y sus sugerencias?

Gracias, Lior