2012-08-04 22 views
13

Deseo implementar el redis de base de almacenamiento de sesión, pongo datos de sesión en redis. Pero no sé cómo manejar la expiración de la sesión. Puedo repetir todas las claves redis (sessionid) y evacuar los datos lastaccess y maxidle, así que necesito cargar todas las claves en el cliente, y tal vez haya claves de sesión de 1000m y pueda liderar el rendimiento de E/S del conjunto.
Wanto deje que el redis administre la caducidad, pero no hay escucha o devolución de llamada cuando expire la clave, por lo que es imposible tirar HttpSessionListener. algún consejo?cómo manejar la sesión expire basando redis?

+0

en Redis, pero es posible que desee echar un vistazo a cómo se hace en Tarantool: https://github.com/mailru/tntlua/blob/master/expirationd.lua En pocas palabras, en el que Tarantool puede ejecutar sus propios scripts de Lua en la base de datos y establecer sus propias políticas de caducidad en ellos. No se necesitan daemons externos. – Kostja

Respuesta

33

Por lo tanto, necesita que se notifique su aplicación cuando caduque una sesión en Redis.

Aunque Redis no es compatible con esta característica, hay una serie de trucos que puede utilizar para implementarlo.

Actualización: A partir de la versión 2.8.0, Redis es compatible con esta http://redis.io/topics/notifications

En primer lugar, las personas están pensando en él, porque éste todavía está en discusión, pero podría ser añadido a una versión futura de Redis. Consulte los siguientes temas:

Ahora, aquí hay algunas soluciones que puede utilizar con las versiones actuales Redis.

Solución 1: parchear Redis

En realidad, la adición de una simple notificación cuando se realiza Redis caducidad de la clave no es tan difícil. Se puede implementar agregando 10 líneas al archivo db.c del código fuente de Redis. Aquí está un ejemplo:

https://gist.github.com/3258233

Este corto mensajes de parche una clave a la lista #expired si la clave ha expirado y comienza con un carácter '@' (elección arbitraria). Se puede adaptar fácilmente a sus necesidades.

Entonces es trivial usar los comandos EXPIRE o SETEX para establecer un tiempo de caducidad para sus objetos de sesión, y escribir un daemon pequeño que repite en BRPOP para quitar de la lista "#expired" y propagar la notificación en su solicitud.

Un punto importante es entender cómo funciona el mecanismo de expiración en Redis. En realidad, hay dos rutas diferentes para la caducidad, ambas activas al mismo tiempo:

  • Mecanismo diferido (pasivo). La caducidad puede ocurrir cada vez que se accede a una tecla.

  • Mecanismo activo. Un trabajo interno regularmente (al azar) muestrea una cantidad de claves con el conjunto de caducidad, tratando de encontrar las que caducan.

Tenga en cuenta que el parche anterior funciona bien en ambas rutas.

La consecuencia es que el tiempo de expiración de Redis no es exacto.Si todas las claves tienen vencimiento, pero solo una está a punto de caducar y no se accede a ella, el trabajo de caducidad activo puede tardar varios minutos en encontrar la clave y expirarla. Si necesita cierta precisión en la notificación, este no es el camino a seguir.

Solución 2: caducidad simulando con zsets

La idea aquí es no confiar en el mecanismo de caducidad de la clave Redis, pero simularlo mediante el uso de un índice adicional, más un demonio de votación. Puede funcionar con una versión Redis 2.6 no modificada.

Cada vez que se añade a una sesión Redis, puede ejecutar:

MULTI 
SET <session id> <session content> 
ZADD to_be_expired <current timestamp + session timeout> <session id> 
EXEC 

El conjunto ordenado to_be_expired es sólo una forma eficiente de acceder a las primeras teclas que deben expirado. Un demonio puede sondear en to_be_expired utilizando la siguiente secuencia de comandos del servidor de Lua:

local res = redis.call('ZRANGEBYSCORE',KEYS[1], 0, ARGV[1], 'LIMIT', 0, 10) 
if #res > 0 then 
    redis.call('ZREMRANGEBYRANK', KEYS[1], 0, #res-1) 
    return res 
else 
    return false 
end 

El comando para iniciar la secuencia de comandos sería:

EVAL <script> 1 to_be_expired <current timestamp> 

El demonio tendrá como máximo 10 elementos. Para cada uno de ellos, tiene que usar el comando DEL para eliminar las sesiones y notificar a la aplicación. Si un elemento se procesó realmente (es decir, el retorno de la secuencia de comandos Lua no está vacío), el daemon debe realizar un ciclo inmediatamente, de lo contrario, se puede introducir un estado de espera de 1 segundo.

Gracias a la secuencia de comandos Lua, es posible ejecutar varios daemons de sondeo en paralelo (la secuencia de comandos garantiza que una sesión determinada solo se procesará una vez, ya que las claves se eliminan de to_be_expired por el script Lua).

Solución 3: usar un temporizador externo distribuido

Otra solución es confiar en un temporizador externo distribuido. El beanstalk lightweight queuing system es una buena posibilidad para este

Cada vez que se agrega una sesión en el sistema, la aplicación envía el ID de sesión a una cola de beans con un retraso correspondiente al tiempo de espera de la sesión. Un daemon está escuchando la cola. Cuando puede develar un artículo, significa que una sesión ha expirado. Solo tiene que limpiar la sesión en Redis y notificar a la aplicación.

No
+0

¡Respuesta increíble, muchas gracias! Sin embargo, puede aclarar esta frase: "el daemon debe repetir el ciclo inmediatamente, de lo contrario, se puede introducir un estado de espera de 1 segundo". ¿Qué significa "looping" en este contexto, y por qué/dónde se introduce esta espera de 1 segundo? –

+0

Los daemons son programas residentes que a veces se despiertan para realizar alguna actividad en el sistema. Como están constantemente en funcionamiento, la mayor parte del código está encerrado en un bucle principal. Ahora un daemon también necesita un estado de espera para evitar tomar 100% de CPU mientras realiza bucles. No hay ningún comando de bloqueo asociado a un zset con Redis (a diferencia de BLPOP/BRPOP para la lista), por lo que debe ser simulado mediante sondeo y durmiendo si no se devuelve nada. –

+2

Esto ya está implementado en redis. Esos problemas están cerrados. Sería bueno si alguien actualiza esta respuesta. –

Cuestiones relacionadas